summary refs log tree commit diff stats
path: root/compiler/sigmatch.nim
diff options
context:
space:
mode:
authorJacek Sieka <arnetheduck@gmail.com>2016-08-25 22:59:51 +0800
committerJacek Sieka <arnetheduck@gmail.com>2016-08-25 22:59:51 +0800
commitdb2f96daba9c04db2f24cb783c79fb37799cd9ea (patch)
tree567beb43c7e4549abfcae1ea66e5232d7525e001 /compiler/sigmatch.nim
parent3116744c86f37ac4e4e5fec3d6d1635304ed717f (diff)
parent84a09d2f5b0866491e55fef0fef541e8cc548852 (diff)
downloadNim-db2f96daba9c04db2f24cb783c79fb37799cd9ea.tar.gz
Merge remote-tracking branch 'origin/devel' into initallocator-fix
Diffstat (limited to 'compiler/sigmatch.nim')
-rw-r--r--compiler/sigmatch.nim214
1 files changed, 152 insertions, 62 deletions
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 82f878932..ec429968c 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -22,7 +22,7 @@ type
   TCandidateState* = enum
     csEmpty, csMatch, csNoMatch
 
-  CandidateErrors* = seq[PSym]
+  CandidateErrors* = seq[(PSym,int)]
   TCandidate* = object
     c*: PContext
     exactMatches*: int       # also misused to prefer iters over procs
@@ -49,6 +49,7 @@ type
                              # a distrinct type
     typedescMatched*: bool
     isNoCall*: bool          # misused for generic type instantiations C[T]
+    mutabilityProblem*: uint8 # tyVar mismatch
     inheritancePenalty: int  # to prefer closest father object type
     errors*: CandidateErrors # additional clarifications to be displayed to the
                              # user if overload resolution fails
@@ -96,8 +97,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
   c.calleeSym = nil
   initIdTable(c.bindings)
 
-proc put(t: var TIdTable, key, val: PType) {.inline.} =
-  idTablePut(t, key, val.skipIntLit)
+proc put(c: var TCandidate, key, val: PType) {.inline.} =
+  idTablePut(c.bindings, key, val.skipIntLit)
 
 proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
                     binding: PNode, calleeScope = -1) =
@@ -129,7 +130,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
           bound = makeTypeDesc(ctx, bound)
       else:
         bound = bound.skipTypes({tyTypeDesc})
-      put(c.bindings, formalTypeParam, bound)
+      put(c, formalTypeParam, bound)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
                    binding: PNode, calleeScope = -1): TCandidate =
@@ -220,13 +221,14 @@ proc cmpCandidates*(a, b: TCandidate): int =
   if result != 0: return
   result = a.convMatches - b.convMatches
   if result != 0: return
-  result = a.calleeScope - b.calleeScope
-  if result != 0: return
   # the other way round because of other semantics:
   result = b.inheritancePenalty - a.inheritancePenalty
   if result != 0: return
   # prefer more specialized generic over more general generic:
   result = complexDisambiguation(a.callee, b.callee)
+  # only as a last resort, consider scoping:
+  if result != 0: return
+  result = a.calleeScope - b.calleeScope
 
 proc writeMatches*(c: TCandidate) =
   writeLine(stdout, "exact matches: " & $c.exactMatches)
@@ -242,6 +244,8 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
     for i in 1 .. <arg.len:
       result.add(" | ")
       result.add typeToString(arg[i].typ, prefer)
+  elif arg.typ == nil:
+    result = "void"
   else:
     result = arg.typ.typeToString(prefer)
 
@@ -253,15 +257,16 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
     if n.sons[i].kind == nkExprEqExpr:
       add(result, renderTree(n.sons[i].sons[0]))
       add(result, ": ")
-      if arg.typ.isNil:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
+        # XXX we really need to 'tryExpr' here!
         arg = c.semOperand(c, n.sons[i].sons[1])
         n.sons[i].typ = arg.typ
         n.sons[i].sons[1] = arg
     else:
-      if arg.typ.isNil:
+      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
         arg = c.semOperand(c, n.sons[i])
         n.sons[i] = arg
-    if arg.typ.kind == tyError: return
+    if arg.typ != nil and arg.typ.kind == tyError: return
     add(result, argTypeToString(arg, prefer))
     if i != sonsLen(n) - 1: add(result, ", ")
 
@@ -360,6 +365,50 @@ proc isObjectSubtype(a, f: PType): int =
   if t != nil:
     result = depth
 
+type
+  SkippedPtr = enum skippedNone, skippedRef, skippedPtr
+
+proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
+  var r = t
+  # we're allowed to skip one level of ptr/ref:
+  var ptrs = 0
+  while r != nil:
+    case r.kind
+    of tyGenericInvocation:
+      r = r.sons[0]
+    of tyRef:
+      inc ptrs
+      skipped = skippedRef
+      r = r.lastSon
+    of tyPtr:
+      inc ptrs
+      skipped = skippedPtr
+      r = r.lastSon
+    of tyGenericBody, tyGenericInst:
+      r = r.lastSon
+    else:
+      break
+  if r.kind == tyObject and ptrs <= 1: result = r
+
+proc isGenericSubtype(a, f: PType, d: var int): bool =
+  assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
+  var askip = skippedNone
+  var fskip = skippedNone
+  var t = a.skipToObject(askip)
+  let r = f.skipToObject(fskip)
+  if r == nil: return false
+  var depth = 0
+  # XXX sameObjectType can return false here. Need to investigate
+  # why that is but sameObjectType does way too much work here anyway.
+  while t != nil and r.sym != t.sym and askip == fskip:
+    t = t.sons[0]
+    if t != nil: t = t.skipToObject(askip)
+    else: break
+    inc depth
+  if t != nil and askip == fskip:
+    d = depth
+    result = true
+
 proc minRel(a, b: TTypeRelation): TTypeRelation =
   if a <= b: result = a
   else: result = b
@@ -513,7 +562,7 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
 proc matchUserTypeClass*(c: PContext, m: var TCandidate,
                          ff, a: PType): TTypeRelation =
   var body = ff.skipTypes({tyUserTypeClassInst})
-  if c.inTypeClass > 20:
+  if c.inTypeClass > 4:
     localError(body.n[3].info, $body.n[3] & " too nested for type matching")
     return isNone
 
@@ -531,7 +580,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
         typ = ff.sons[i]
         param: PSym
 
-      template paramSym(kind): expr =
+      template paramSym(kind): untyped =
         newSym(kind, typeParamName, body.sym, body.sym.info)
 
       case typ.kind
@@ -598,6 +647,10 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
   let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil)
   result = c.c.semExpr(c.c, instantiated)
 
+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 =
   # typeRel can be used to establish various relationships between types:
   #
@@ -619,7 +672,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   assert(f != nil)
 
   if f.kind == tyExpr:
-    if aOrig != nil: put(c.bindings, f, aOrig)
+    if aOrig != nil: put(c, f, aOrig)
     return isGeneric
 
   assert(aOrig != nil)
@@ -640,10 +693,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   template bindingRet(res) =
     if doBind:
       let bound = aOrig.skipTypes({tyRange}).skipIntLit
-      if doBind: put(c.bindings, f, bound)
+      put(c, f, bound)
     return res
 
-  template considerPreviousT(body: stmt) {.immediate.} =
+  template considerPreviousT(body: untyped) =
     var prev = PType(idTableGet(c.bindings, f))
     if prev == nil: body
     else: return typeRel(c, prev, a)
@@ -737,6 +790,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyVar:
     if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base)
     else: result = typeRel(c, f.base, aOrig)
+    subtypeCheck()
   of tyArray, tyArrayConstr:
     # tyArrayConstr cannot happen really, but
     # we wanna be safe here
@@ -746,7 +800,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if fRange.kind == tyGenericParam:
         var prev = PType(idTableGet(c.bindings, fRange))
         if prev == nil:
-          put(c.bindings, fRange, a.sons[0])
+          put(c, fRange, a.sons[0])
           fRange = a
         else:
           fRange = prev
@@ -764,7 +818,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           # 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)
+          put(c, rangeStaticT, replacementT)
           return isGeneric
 
         let len = tryResolvingStaticExpr(c, fRange.n[1])
@@ -851,6 +905,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if sameDistinctTypes(f, a): result = isEqual
       elif f.base.kind == tyAnything: result = isGeneric
       elif c.coerceDistincts: result = typeRel(c, f.base, a)
+    elif a.kind == tyNil and f.base.kind in NilableTypes:
+      result = f.allowsNil
     elif c.coerceDistincts: result = typeRel(c, f.base, a)
   of tySet:
     if a.kind == tySet:
@@ -867,6 +923,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       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)
+      subtypeCheck()
       if result <= isConvertible: result = isNone
       elif tfNotNil in f.flags and tfNotNil notin a.flags:
         result = isNilConversion
@@ -921,8 +978,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isConvertible
     else: discard
 
-  of tyEmpty:
-    if a.kind == tyEmpty: result = isEqual
+  of tyEmpty, tyVoid:
+    if a.kind == f.kind: result = isEqual
 
   of tyGenericInst:
     result = typeRel(c, lastSon(f), a)
@@ -932,21 +989,26 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if a.kind == tyGenericInst and a.sons[0] == f:
         bindingRet isGeneric
       let ff = lastSon(f)
-      if ff != nil: result = typeRel(c, ff, a)
+      if ff != nil:
+        result = typeRel(c, ff, a)
 
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+    var depth = 0
     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
-          (f.sons[0] == x.sons[0]) and
+          ((f.sons[0] == x.sons[0]) or isGenericSubtype(x, f, depth)) 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:
+          # Workaround for regression #4589
+          if f.sons[i].kind != tyTypeDesc: return
+      c.inheritancePenalty += depth
       result = isGeneric
     else:
       let genericBody = f.sons[0]
@@ -963,13 +1025,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         #
         # we steal the generic parameters from the tyGenericBody:
         for i in countup(1, sonsLen(f) - 1):
-          var x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
+          let 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!")
           else:
-            put(c.bindings, f.sons[i], x)
+            put(c, f.sons[i], x)
 
   of tyAnd:
     considerPreviousT:
@@ -979,6 +1041,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         if x < isSubtype: return isNone
         # 'and' implies minimum matching result:
         if x < result: result = x
+      if result > isGeneric: result = isGeneric
       bindingRet result
 
   of tyOr:
@@ -989,6 +1052,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         # 'or' implies maximum matching result:
         if x > result: result = x
       if result >= isSubtype:
+        if result > isGeneric: result = isGeneric
         bindingRet result
       else:
         result = isNone
@@ -1005,7 +1069,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     considerPreviousT:
       var concrete = concreteType(c, a)
       if concrete != nil and doBind:
-        put(c.bindings, f, concrete)
+        put(c, f, concrete)
       return isGeneric
 
   of tyBuiltInTypeClass:
@@ -1013,7 +1077,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       let targetKind = f.sons[0].kind
       if targetKind == a.skipTypes({tyRange, tyGenericInst}).kind or
          (targetKind in {tyProc, tyPointer} and a.kind == tyNil):
-        put(c.bindings, f, a)
+        put(c, f, a)
         return isGeneric
       else:
         return isNone
@@ -1022,7 +1086,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     considerPreviousT:
       result = matchUserTypeClass(c.c, c, f, aOrig)
       if result == isGeneric:
-        put(c.bindings, f, a)
+        put(c, f, a)
 
   of tyCompositeTypeClass:
     considerPreviousT:
@@ -1038,7 +1102,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       else:
         result = typeRel(c, rootf.lastSon, a)
       if result != isNone:
-        put(c.bindings, f, a)
+        put(c, f, a)
         result = isGeneric
   of tyGenericParam:
     var x = PType(idTableGet(c.bindings, f))
@@ -1072,7 +1136,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           if doBind and result notin {isNone, isGeneric}:
             let concrete = concreteType(c, a)
             if concrete == nil: return isNone
-            put(c.bindings, f, concrete)
+            put(c, f, concrete)
         else:
           result = isGeneric
 
@@ -1086,7 +1150,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           if concrete == nil:
             return isNone
         if doBind:
-          put(c.bindings, f, concrete)
+          put(c, f, concrete)
       elif result > isGeneric:
         result = isGeneric
     elif a.kind == tyEmpty:
@@ -1105,7 +1169,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         if result != isNone and f.n != nil:
           if not exprStructuralEquivalent(f.n, aOrig.n):
             result = isNone
-        if result != isNone: put(c.bindings, f, aOrig)
+        if result != isNone: put(c, f, aOrig)
       else:
         result = isNone
     elif prev.kind == tyStatic:
@@ -1133,7 +1197,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = typeRel(c, f.base, a.base)
 
       if result != isNone:
-        put(c.bindings, f, a)
+        put(c, f, a)
     else:
       if tfUnresolved in f.flags:
         result = typeRel(c, prev.base, a)
@@ -1151,7 +1215,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   of tyStmt:
     if aOrig != nil and tfOldSchoolExprStmt notin f.flags:
-      put(c.bindings, f, aOrig)
+      put(c, f, aOrig)
     result = isGeneric
 
   of tyProxy:
@@ -1256,6 +1320,13 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
     if r == isGeneric:
       result.typ = getInstantiatedType(c, arg, m, base(f))
     m.baseTypeMatch = true
+    # bug #4545: allow the call to go through a 'var T':
+    let vt = result.sons[0].typ.sons[1]
+    if vt.kind == tyVar:
+      let x = result.sons[1]
+      let va = newNodeIT(nkHiddenAddr, x.info, vt)
+      va.add x
+      result.sons[1] = va
 
 proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   case r
@@ -1399,7 +1470,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isNone:
-    # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
+    # do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
     if a.kind in {tyProxy, tyUnknown}:
       inc(m.genericMatches)
       m.fauxMatch = a.kind
@@ -1420,6 +1491,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
           m.baseTypeMatch = true
         else:
           result = userConvMatch(c, m, base(f), a, arg)
+          if result != nil: m.baseTypeMatch = true
 
 proc paramTypesMatch*(m: var TCandidate, f, a: PType,
                       arg, argOrig: PNode): PNode =
@@ -1532,13 +1604,13 @@ proc incrIndexType(t: PType) =
   assert t.kind == tyArrayConstr
   inc t.sons[0].n.sons[1].intVal
 
-template isVarargsUntyped(x): expr =
+template isVarargsUntyped(x): untyped =
   x.kind == tyVarargs and x.sons[0].kind == tyExpr and
     tfOldSchoolExprStmt notin x.sons[0].flags
 
 proc matchesAux(c: PContext, n, nOrig: PNode,
                 m: var TCandidate, marker: var IntSet) =
-  template checkConstraint(n: expr) {.immediate, dirty.} =
+  template checkConstraint(n: untyped) {.dirty.} =
     if not formal.constraint.isNil:
       if matchNodeKinds(formal.constraint, n):
         # better match over other routines with no such restriction:
@@ -1549,6 +1621,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
     if formal.typ.kind == tyVar:
       if not n.isLValue:
         m.state = csNoMatch
+        m.mutabilityProblem = uint8(f-1)
         return
 
   var
@@ -1568,6 +1641,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
 
   while a < n.len:
     if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped:
+      incl(marker, formal.position)
       if container.isNil:
         container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
         setSon(m.call, formal.position + 1, container)
@@ -1630,6 +1704,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           # beware of the side-effects in 'prepareOperand'! So only do it for
           # varargs matching. See tests/metatype/tstatic_overloading.
           m.baseTypeMatch = false
+          incl(marker, formal.position)
           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])
@@ -1653,30 +1728,39 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
           m.state = csNoMatch
           return
-        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:
-          m.state = csNoMatch
-          return
-        if m.baseTypeMatch:
-          #assert(container == nil)
+
+        if formal.typ.isVarargsUntyped:
           if container.isNil:
-            container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+            container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info))
+            setSon(m.call, formal.position + 1, container)
           else:
             incrIndexType(container.typ)
-          addSon(container, arg)
-          setSon(m.call, formal.position + 1,
-                 implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
-          #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)
+          addSon(container, n.sons[a])
         else:
-          setSon(m.call, formal.position + 1, arg)
-          inc(f)
-          container = nil
+          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:
+            m.state = csNoMatch
+            return
+          if m.baseTypeMatch:
+            #assert(container == nil)
+            if container.isNil:
+              container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+            else:
+              incrIndexType(container.typ)
+            addSon(container, arg)
+            setSon(m.call, formal.position + 1,
+                   implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
+            #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)
 
@@ -1707,15 +1791,18 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
       if formal.ast == nil:
         if formal.typ.kind == tyVarargs:
           var container = newNodeIT(nkBracket, n.info, arrayConstr(c, n.info))
-          addSon(m.call, implicitConv(nkHiddenStdConv, formal.typ,
-                                      container, m, c))
+          setSon(m.call, formal.position + 1,
+                 implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
         else:
           # no default value
           m.state = csNoMatch
           break
       else:
         # use default value:
-        setSon(m.call, formal.position + 1, copyTree(formal.ast))
+        var def = copyTree(formal.ast)
+        if def.kind == nkNilLit:
+          def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
+        setSon(m.call, formal.position + 1, def)
     inc(f)
 
 proc argtypeMatches*(c: PContext, f, a: PType): bool =
@@ -1728,16 +1815,19 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
   result = res != nil
 
 proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
-                      op: TTypeAttachedOp): PSym {.procvar.} =
+                      op: TTypeAttachedOp; col: int): PSym {.procvar.} =
   var m: TCandidate
   initCandidate(c, m, dc.typ)
-  var f = dc.typ.sons[1]
+  if col >= dc.typ.len:
+    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:
     if f.kind == tyVar: f = f.lastSon
   if typeRel(m, f, t) == isNone:
-    localError(info, errGenerated, "cannot instantiate 'deepCopy'")
+    localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
   else:
     result = c.semGenerateInstance(c, dc, m.bindings, info)
     assert sfFromGeneric in result.flags
@@ -1745,7 +1835,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
 include suggest
 
 when not declared(tests):
-  template tests(s: stmt) {.immediate.} = discard
+  template tests(s: untyped) = discard
 
 tests:
   var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo())