summary refs log tree commit diff stats
path: root/rod/sigmatch.nim
diff options
context:
space:
mode:
Diffstat (limited to 'rod/sigmatch.nim')
-rwxr-xr-xrod/sigmatch.nim750
1 files changed, 750 insertions, 0 deletions
diff --git a/rod/sigmatch.nim b/rod/sigmatch.nim
new file mode 100755
index 000000000..1bb68ef1a
--- /dev/null
+++ b/rod/sigmatch.nim
@@ -0,0 +1,750 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2009 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+# This module implements the signature matching for resolving
+# the call to overloaded procs, generic procs and operators.
+
+type 
+  TCandidateState = enum 
+    csEmpty, csMatch, csNoMatch
+  TCandidate{.final.} = object 
+    exactMatches*: int
+    subtypeMatches*: int
+    intConvMatches*: int      # conversions to int are not as expensive
+    convMatches*: int
+    genericMatches*: int
+    state*: TCandidateState
+    callee*: PType            # may not be nil!
+    calleeSym*: PSym          # may be nil
+    call*: PNode              # modified call
+    bindings*: TIdTable       # maps sym-ids to types
+    baseTypeMatch*: bool      # needed for conversions from T to openarray[T]
+                              # for example
+  
+  TTypeRelation = enum        # order is important!
+    isNone, isConvertible, isIntConv, isSubtype, isGeneric, isEqual
+
+proc initCandidate(c: var TCandidate, callee: PType) = 
+  c.exactMatches = 0
+  c.subtypeMatches = 0
+  c.convMatches = 0
+  c.intConvMatches = 0
+  c.genericMatches = 0
+  c.state = csEmpty
+  c.callee = callee
+  c.calleeSym = nil
+  c.call = nil
+  c.baseTypeMatch = false
+  initIdTable(c.bindings)     #assert(c.callee <> nil);
+  
+proc copyCandidate(a: var TCandidate, b: TCandidate) = 
+  a.exactMatches = b.exactMatches
+  a.subtypeMatches = b.subtypeMatches
+  a.convMatches = b.convMatches
+  a.intConvMatches = b.intConvMatches
+  a.genericMatches = b.genericMatches
+  a.state = b.state
+  a.callee = b.callee
+  a.calleeSym = b.calleeSym
+  a.call = copyTree(b.call)
+  a.baseTypeMatch = b.baseTypeMatch
+  copyIdTable(a.bindings, b.bindings)
+
+proc cmpCandidates(a, b: TCandidate): int = 
+  result = a.exactMatches - b.exactMatches
+  if result != 0: return 
+  result = a.genericMatches - b.genericMatches
+  if result != 0: return 
+  result = a.subtypeMatches - b.subtypeMatches
+  if result != 0: return 
+  result = a.intConvMatches - b.intConvMatches
+  if result != 0: return 
+  result = a.convMatches - b.convMatches
+
+proc writeMatches(c: TCandidate) = 
+  Writeln(stdout, "exact matches: " & $(c.exactMatches))
+  Writeln(stdout, "subtype matches: " & $(c.subtypeMatches))
+  Writeln(stdout, "conv matches: " & $(c.convMatches))
+  Writeln(stdout, "intconv matches: " & $(c.intConvMatches))
+  Writeln(stdout, "generic matches: " & $(c.genericMatches))
+
+proc getNotFoundError(c: PContext, n: PNode): string = 
+  # Gives a detailed error message; this is seperated from semDirectCall,
+  # as semDirectCall is already pretty slow (and we need this information only
+  # in case of an error).
+  var 
+    sym: PSym
+    o: TOverloadIter
+    candidates: string
+  result = msgKindToString(errTypeMismatch)
+  for i in countup(1, sonsLen(n) - 1): 
+    #debug(n.sons[i].typ);
+    add(result, typeToString(n.sons[i].typ))
+    if i != sonsLen(n) - 1: add(result, ", ")
+  add(result, ')')
+  candidates = ""
+  sym = initOverloadIter(o, c, n.sons[0])
+  while sym != nil: 
+    if sym.kind in {skProc, skMethod, skIterator, skConverter}: 
+      add(candidates, getProcHeader(sym))
+      add(candidates, "\n")
+    sym = nextOverloadIter(o, c, n.sons[0])
+  if candidates != "": 
+    add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
+  
+proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation
+proc concreteType(mapping: TIdTable, t: PType): PType = 
+  case t.kind
+  of tyArrayConstr: 
+    # make it an array
+    result = newType(tyArray, t.owner)
+    addSon(result, t.sons[0]) # XXX: t.owner is wrong for ID!
+    addSon(result, t.sons[1]) # XXX: semantic checking for the type?
+  of tyNil: 
+    result = nil              # what should it be?
+  of tyGenericParam: 
+    result = t
+    while true: 
+      result = PType(idTableGet(mapping, t))
+      if result == nil: InternalError("lookup failed")
+      if result.kind != tyGenericParam: break 
+  else: 
+    result = t                # Note: empty is valid here
+  
+proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = 
+  var k: TTypeKind
+  if a.kind == f.kind: 
+    result = isEqual
+  else: 
+    k = skipTypes(a, {tyRange}).kind
+    if k == f.kind: result = isSubtype
+    elif (f.kind == tyInt) and (k in {tyInt..tyInt32}): result = isIntConv
+    elif (k >= min) and (k <= max): result = isConvertible
+    else: result = isNone
+  
+proc handleFloatRange(f, a: PType): TTypeRelation = 
+  var k: TTypeKind
+  if a.kind == f.kind: 
+    result = isEqual
+  else: 
+    k = skipTypes(a, {tyRange}).kind
+    if k == f.kind: result = isSubtype
+    elif (k >= tyFloat) and (k <= tyFloat128): result = isConvertible
+    else: result = isNone
+  
+proc isObjectSubtype(a, f: PType): bool = 
+  var t: PType
+  t = a
+  while (t != nil) and (t.id != f.id): t = base(t)
+  result = t != nil
+
+proc minRel(a, b: TTypeRelation): TTypeRelation = 
+  if a <= b: result = a
+  else: result = b
+  
+proc tupleRel(mapping: var TIdTable, f, a: PType): TTypeRelation = 
+  var 
+    x, y: PSym
+    m: TTypeRelation
+  result = isNone
+  if sonsLen(a) == sonsLen(f): 
+    result = isEqual
+    for i in countup(0, sonsLen(f) - 1): 
+      m = typeRel(mapping, f.sons[i], a.sons[i])
+      if m < isSubtype: 
+        return isNone
+      result = minRel(result, m)
+    if (f.n != nil) and (a.n != nil): 
+      for i in countup(0, sonsLen(f.n) - 1): 
+        # check field names:
+        if f.n.sons[i].kind != nkSym: InternalError(f.n.info, "tupleRel")
+        if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "tupleRel")
+        x = f.n.sons[i].sym
+        y = a.n.sons[i].sym
+        if x.name.id != y.name.id: 
+          return isNone
+
+proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation = 
+  var 
+    x, concrete: PType
+    m: TTypeRelation
+  # is a subtype of f?
+  result = isNone
+  assert(f != nil)
+  assert(a != nil)
+  if (a.kind == tyGenericInst) and
+      not (skipTypes(f, {tyVar}).kind in {tyGenericBody, tyGenericInvokation}): 
+    return typeRel(mapping, f, lastSon(a))
+  if (a.kind == tyVar) and (f.kind != tyVar): 
+    return typeRel(mapping, f, a.sons[0])
+  case f.kind
+  of tyEnum: 
+    if (a.kind == f.kind) and (a.id == f.id): result = isEqual
+    elif (skipTypes(a, {tyRange}).id == f.id): result = isSubtype
+  of tyBool, tyChar: 
+    if (a.kind == f.kind): result = isEqual
+    elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
+  of tyRange: 
+    if (a.kind == f.kind): 
+      result = typeRel(mapping, base(a), base(f))
+      if result < isGeneric: result = isNone
+    elif skipTypes(f, {tyRange}).kind == a.kind: 
+      result = isConvertible  # a convertible to f
+  of tyInt: 
+    result = handleRange(f, a, tyInt8, tyInt32)
+  of tyInt8: 
+    result = handleRange(f, a, tyInt8, tyInt8)
+  of tyInt16: 
+    result = handleRange(f, a, tyInt8, tyInt16)
+  of tyInt32: 
+    result = handleRange(f, a, tyInt, tyInt32)
+  of tyInt64: 
+    result = handleRange(f, a, tyInt, tyInt64)
+  of tyFloat: 
+    result = handleFloatRange(f, a)
+  of tyFloat32: 
+    result = handleFloatRange(f, a)
+  of tyFloat64: 
+    result = handleFloatRange(f, a)
+  of tyFloat128: 
+    result = handleFloatRange(f, a)
+  of tyVar: 
+    if (a.kind == f.kind): result = typeRel(mapping, base(f), base(a))
+    else: result = typeRel(mapping, base(f), a)
+  of tyArray, tyArrayConstr: 
+    # tyArrayConstr cannot happen really, but
+    # we wanna be safe here
+    case a.kind
+    of tyArray: 
+      result = minRel(typeRel(mapping, f.sons[0], a.sons[0]), 
+                      typeRel(mapping, f.sons[1], a.sons[1]))
+      if result < isGeneric: result = isNone
+    of tyArrayConstr: 
+      result = typeRel(mapping, f.sons[1], a.sons[1])
+      if result < isGeneric: 
+        result = isNone
+      else: 
+        if (result != isGeneric) and (lengthOrd(f) != lengthOrd(a)): 
+          result = isNone
+        elif f.sons[0].kind in GenericTypes: 
+          result = minRel(result, typeRel(mapping, f.sons[0], a.sons[0]))
+    else: 
+      nil
+  of tyOpenArray: 
+    case a.Kind
+    of tyOpenArray: 
+      result = typeRel(mapping, base(f), base(a))
+      if result < isGeneric: result = isNone
+    of tyArrayConstr: 
+      if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): 
+        result = isSubtype    # [] is allowed here
+      elif typeRel(mapping, base(f), a.sons[1]) >= isGeneric: 
+        result = isSubtype
+    of tyArray: 
+      if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): 
+        result = isSubtype
+      elif typeRel(mapping, base(f), a.sons[1]) >= isGeneric: 
+        result = isConvertible
+    of tySequence: 
+      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): 
+        result = isConvertible
+      elif typeRel(mapping, base(f), a.sons[0]) >= isGeneric: 
+        result = isConvertible
+    else: 
+      nil
+  of tySequence: 
+    case a.Kind
+    of tyNil: 
+      result = isSubtype
+    of tySequence: 
+      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): 
+        result = isSubtype
+      else: 
+        result = typeRel(mapping, f.sons[0], a.sons[0])
+        if result < isGeneric: result = isNone
+    else: 
+      nil
+  of tyOrdinal: 
+    if isOrdinalType(a): 
+      if a.kind == tyOrdinal: x = a.sons[0]
+      else: x = a
+      result = typeRel(mapping, f.sons[0], x)
+      if result < isGeneric: result = isNone
+  of tyForward: 
+    InternalError("forward type in typeRel()")
+  of tyNil: 
+    if a.kind == f.kind: result = isEqual
+  of tyTuple: 
+    if a.kind == tyTuple: result = tupleRel(mapping, f, a)
+  of tyObject: 
+    if a.kind == tyObject: 
+      if a.id == f.id: result = isEqual
+      elif isObjectSubtype(a, f): result = isSubtype
+  of tyDistinct: 
+    if (a.kind == tyDistinct) and (a.id == f.id): result = isEqual
+  of tySet: 
+    if a.kind == tySet: 
+      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): 
+        result = isSubtype
+      else: 
+        result = typeRel(mapping, f.sons[0], a.sons[0])
+        if result <= isConvertible: 
+          result = isNone     # BUGFIX!
+  of tyPtr: 
+    case a.kind
+    of tyPtr: 
+      result = typeRel(mapping, base(f), base(a))
+      if result <= isConvertible: result = isNone
+    of tyNil: 
+      result = isSubtype
+    else: 
+      nil
+  of tyRef: 
+    case a.kind
+    of tyRef: 
+      result = typeRel(mapping, base(f), base(a))
+      if result <= isConvertible: result = isNone
+    of tyNil: 
+      result = isSubtype
+    else: 
+      nil
+  of tyProc: 
+    case a.kind
+    of tyNil: 
+      result = isSubtype
+    of tyProc: 
+      if (sonsLen(f) == sonsLen(a)) and (f.callconv == a.callconv): 
+        # Note: We have to do unification for the parameters before the
+        # return type!
+        result = isEqual      # start with maximum; also correct for no
+                              # params at all
+        for i in countup(1, sonsLen(f) - 1): 
+          m = typeRel(mapping, f.sons[i], a.sons[i])
+          if (m == isNone) and
+              (typeRel(mapping, a.sons[i], f.sons[i]) == isSubtype): 
+            # allow ``f.son`` as subtype of ``a.son``!
+            result = isConvertible
+          elif m < isSubtype: 
+            return isNone
+          else: 
+            result = minRel(m, result)
+        if f.sons[0] != nil: 
+          if a.sons[0] != nil: 
+            m = typeRel(mapping, f.sons[0], a.sons[0]) # Subtype is sufficient for return types!
+            if m < isSubtype: result = isNone
+            elif m == isSubtype: result = isConvertible
+            else: result = minRel(m, result)
+          else: 
+            result = isNone
+        elif a.sons[0] != nil: 
+          result = isNone
+        if (tfNoSideEffect in f.flags) and not (tfNoSideEffect in a.flags): 
+          result = isNone
+    else: 
+      nil
+  of tyPointer: 
+    case a.kind
+    of tyPointer: result = isEqual
+    of tyNil: result = isSubtype
+    of tyRef, tyPtr, tyProc, tyCString: result = isConvertible
+    else: 
+      nil
+  of tyString: 
+    case a.kind
+    of tyString: result = isEqual
+    of tyNil: result = isSubtype
+    else: 
+      nil
+  of tyCString: 
+    # conversion from string to cstring is automatic:
+    case a.Kind
+    of tyCString: 
+      result = isEqual
+    of tyNil: 
+      result = isSubtype
+    of tyString: 
+      result = isConvertible
+    of tyPtr: 
+      if a.sons[0].kind == tyChar: result = isConvertible
+    of tyArray: 
+      if (firstOrd(a.sons[0]) == 0) and
+          (skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
+          (a.sons[1].kind == tyChar): 
+        result = isConvertible
+    else: 
+      nil
+  of tyEmpty: 
+    if a.kind == tyEmpty: result = isEqual
+  of tyGenericInst: 
+    result = typeRel(mapping, lastSon(f), a)
+  of tyGenericBody: 
+    result = typeRel(mapping, lastSon(f), a)
+  of tyGenericInvokation: 
+    assert(f.sons[0].kind == tyGenericBody)
+    if a.kind == tyGenericInvokation: 
+      InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation")
+    if (a.kind == tyGenericInst): 
+      if (f.sons[0].containerID == a.sons[0].containerID) and
+          (sonsLen(a) - 1 == sonsLen(f)): 
+        assert(a.sons[0].kind == tyGenericBody)
+        for i in countup(1, sonsLen(f) - 1): 
+          if a.sons[i].kind == tyGenericParam: 
+            InternalError("wrong instantiated type!")
+          if typeRel(mapping, f.sons[i], a.sons[i]) < isGeneric: return 
+        result = isGeneric
+    else: 
+      result = typeRel(mapping, f.sons[0], a)
+      if result != isNone: 
+        # we steal the generic parameters from the tyGenericBody:
+        for i in countup(1, sonsLen(f) - 1): 
+          x = PType(idTableGet(mapping, f.sons[0].sons[i - 1]))
+          if (x == nil) or (x.kind == tyGenericParam): 
+            InternalError("wrong instantiated type!")
+          idTablePut(mapping, f.sons[i], x)
+  of tyGenericParam: 
+    x = PType(idTableGet(mapping, f))
+    if x == nil: 
+      if sonsLen(f) == 0: 
+        # no constraints
+        concrete = concreteType(mapping, a)
+        if concrete != nil: 
+          #MessageOut('putting: ' + f.sym.name.s);
+          idTablePut(mapping, f, concrete)
+          result = isGeneric
+      else: 
+        InternalError(f.sym.info, "has constraints: " & f.sym.name.s) # check 
+                                                                      # constraints:
+        for i in countup(0, sonsLen(f) - 1): 
+          if typeRel(mapping, f.sons[i], a) >= isSubtype: 
+            concrete = concreteType(mapping, a)
+            if concrete != nil: 
+              idTablePut(mapping, f, concrete)
+              result = isGeneric
+            break 
+    elif a.kind == tyEmpty: 
+      result = isGeneric
+    elif x.kind == tyGenericParam: 
+      result = isGeneric
+    else: 
+      result = typeRel(mapping, x, a) # check if it fits
+  of tyExpr, tyStmt, tyTypeDesc: 
+    if a.kind == f.kind: 
+      result = isEqual
+    else: 
+      case a.kind
+      of tyExpr, tyStmt, tyTypeDesc: result = isGeneric
+      of tyNil: result = isSubtype
+      else: 
+        nil
+  else: internalError("typeRel(" & $f.kind & ')')
+  
+proc cmpTypes(f, a: PType): TTypeRelation = 
+  var mapping: TIdTable
+  InitIdTable(mapping)
+  result = typeRel(mapping, f, a)
+
+proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, 
+                         f: PType): PType = 
+  result = PType(idTableGet(m.bindings, f))
+  if result == nil: 
+    result = generateTypeInstance(c, m.bindings, arg, f)
+  if result == nil: InternalError(arg.info, "getInstantiatedType")
+  
+proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, 
+                  c: PContext): PNode = 
+  result = newNodeI(kind, arg.info)
+  if containsGenericType(f): result.typ = getInstantiatedType(c, arg, m, f)
+  else: result.typ = f
+  if result.typ == nil: InternalError(arg.info, "implicitConv")
+  addSon(result, nil)
+  addSon(result, arg)
+
+proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, 
+                   arg: PNode): PNode = 
+  result = nil
+  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]
+    if (typeRel(m.bindings, f, dest) == isEqual) and
+        (typeRel(m.bindings, src, a) == isEqual): 
+      var s = newSymNode(c.converters[i])
+      s.typ = c.converters[i].typ
+      s.info = arg.info
+      result = newNodeIT(nkHiddenCallConv, arg.info, s.typ.sons[0])
+      addSon(result, s)
+      addSon(result, copyTree(arg))
+      inc(m.convMatches)
+      return 
+
+proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 
+                        arg: PNode): PNode = 
+  var r = typeRel(m.bindings, f, a)
+  case r
+  of isConvertible: 
+    inc(m.convMatches)
+    result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+  of isIntConv: 
+    inc(m.intConvMatches)
+    result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+  of isSubtype: 
+    inc(m.subtypeMatches)
+    result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
+  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).kind in {tyTuple, tyOpenArray}): 
+      result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) 
+      # BUGFIX: use ``result.typ`` and not `f` here
+  of isEqual: 
+    inc(m.exactMatches)
+    result = copyTree(arg)
+    if (skipTypes(f, abstractVar).kind in {tyTuple, tyOpenArray}): 
+      result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+  of isNone: 
+    result = userConvMatch(c, m, f, a, arg) 
+    # check for a base type match, which supports openarray[T] without []
+    # constructor in a call:
+    if (result == nil) and (f.kind == tyOpenArray): 
+      r = typeRel(m.bindings, base(f), a)
+      if r >= isGeneric: 
+        inc(m.convMatches)
+        result = copyTree(arg)
+        if r == isGeneric: result.typ = getInstantiatedType(c, arg, m, base(f))
+        m.baseTypeMatch = true
+      else: 
+        result = userConvMatch(c, m, base(f), a, arg)
+  
+proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType, 
+                     arg: PNode): PNode = 
+  var 
+    cmp, best: int
+    x, y, z: TCandidate
+    r: TTypeRelation
+  if (arg == nil) or (arg.kind != nkSymChoice): 
+    result = ParamTypesMatchAux(c, m, f, a, arg)
+  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.
+    initCandidate(x, m.callee)
+    initCandidate(y, m.callee)
+    initCandidate(z, m.callee)
+    x.calleeSym = m.calleeSym
+    y.calleeSym = m.calleeSym
+    z.calleeSym = m.calleeSym
+    best = - 1
+    for i in countup(0, sonsLen(arg) - 1): 
+      # iterators are not first class yet, so ignore them
+      if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}: 
+        copyCandidate(z, m)
+        r = typeRel(z.bindings, f, arg.sons[i].typ)
+        if r != isNone: 
+          case x.state
+          of csEmpty, csNoMatch: 
+            x = z
+            best = i
+            x.state = csMatch
+          of csMatch: 
+            cmp = cmpCandidates(x, z)
+            if cmp < 0: 
+              best = i
+              x = z
+            elif cmp == 0: 
+              y = z           # z is as good as x
+            else: 
+              nil
+    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
+      result = nil
+    else: 
+      # only one valid interpretation found:
+      markUsed(arg, arg.sons[best].sym)
+      result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best])
+
+proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = 
+  var m: TCandidate
+  initCandidate(m, f)
+  result = paramTypesMatch(c, m, f, a, arg)
+
+proc setSon(father: PNode, at: int, son: PNode) = 
+  if sonsLen(father) <= at: setlen(father.sons, at + 1)
+  father.sons[at] = son
+
+proc matches(c: PContext, n: PNode, m: var TCandidate) = 
+  var f = 1 # iterates over formal parameters
+  var a = 1 # iterates over the actual given arguments
+  m.state = csMatch           # until proven otherwise
+  m.call = newNodeI(nkCall, n.info)
+  m.call.typ = base(m.callee) # may be nil
+  var formalLen = sonsLen(m.callee.n)
+  addSon(m.call, copyTree(n.sons[0]))
+  var marker: TIntSet
+  IntSetInit(marker)
+  var container: PNode = nil # constructed container
+  var formal: PSym = nil
+  while a < sonsLen(n): 
+    if n.sons[a].kind == nkExprEqExpr: 
+      # named param
+      # check if m.callee has such a param:
+      if n.sons[a].sons[0].kind != nkIdent: 
+        liMessage(n.sons[a].info, errNamedParamHasToBeIdent)
+        m.state = csNoMatch
+        return 
+      formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
+      if formal == nil: 
+        # no error message!
+        m.state = csNoMatch
+        return 
+      if IntSetContainsOrIncl(marker, formal.position): 
+        # already in namedParams:
+        liMessage(n.sons[a].info, errCannotBindXTwice, formal.name.s)
+        m.state = csNoMatch
+        return 
+      m.baseTypeMatch = false
+      var arg = ParamTypesMatch(c, m, formal.typ, 
+                                      n.sons[a].typ, n.sons[a].sons[1])
+      if (arg == nil): 
+        m.state = csNoMatch
+        return 
+      if m.baseTypeMatch: 
+        assert(container == nil)
+        container = newNodeI(nkBracket, n.sons[a].info)
+        addSon(container, arg)
+        setSon(m.call, formal.position + 1, container)
+        if f != formalLen - 1: container = nil
+      else: 
+        setSon(m.call, formal.position + 1, arg)
+    else: 
+      # unnamed param
+      if f >= formalLen: 
+        # too many arguments?
+        if tfVarArgs in m.callee.flags: 
+          # is ok... but don't increment any counters...
+          if skipTypes(n.sons[a].typ, abstractVar).kind == tyString: 
+            addSon(m.call, implicitConv(nkHiddenStdConv, getSysType(tyCString), 
+                                        copyTree(n.sons[a]), m, c))
+          else: 
+            addSon(m.call, copyTree(n.sons[a]))
+        elif formal != nil: 
+          m.baseTypeMatch = false
+          var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a])
+          if (arg != nil) and m.baseTypeMatch and (container != nil): 
+            addSon(container, arg)
+          else: 
+            m.state = csNoMatch
+            return 
+        else: 
+          m.state = csNoMatch
+          return 
+      else: 
+        if m.callee.n.sons[f].kind != nkSym: 
+          InternalError(n.sons[a].info, "matches")
+        formal = m.callee.n.sons[f].sym
+        if IntSetContainsOrIncl(marker, formal.position): 
+          # already in namedParams:
+          liMessage(n.sons[a].info, errCannotBindXTwice, formal.name.s)
+          m.state = csNoMatch
+          return 
+        m.baseTypeMatch = false
+        var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a])
+        if (arg == nil): 
+          m.state = csNoMatch
+          return 
+        if m.baseTypeMatch: 
+          assert(container == nil)
+          container = newNodeI(nkBracket, n.sons[a].info)
+          addSon(container, arg)
+          setSon(m.call, formal.position + 1, 
+                 implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
+          if f != formalLen - 1: container = nil
+        else: 
+          setSon(m.call, formal.position + 1, arg)
+    inc(a)
+    inc(f)
+  f = 1
+  while f < sonsLen(m.callee.n): 
+    formal = m.callee.n.sons[f].sym
+    if not IntSetContainsOrIncl(marker, formal.position): 
+      if formal.ast == nil: 
+        # no default value
+        m.state = csNoMatch
+        break 
+      else: 
+        # use default value:
+        setSon(m.call, formal.position + 1, copyTree(formal.ast))
+    inc(f)
+
+proc sameMethodDispatcher(a, b: PSym): bool = 
+  result = false
+  if (a.kind == skMethod) and (b.kind == skMethod): 
+    var aa = lastSon(a.ast)
+    var bb = lastSon(b.ast)
+    if (aa.kind == nkSym) and (bb.kind == nkSym) and (aa.sym == bb.sym): 
+      result = true
+  
+proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode = 
+  var 
+    o: TOverloadIter
+    x, y, z: TCandidate
+  #liMessage(n.info, warnUser, renderTree(n));
+  var sym = initOverloadIter(o, c, n.sons[0])
+  result = nil
+  if sym == nil: return 
+  initCandidate(x, sym.typ)
+  x.calleeSym = sym
+  initCandidate(y, sym.typ)
+  y.calleeSym = sym
+  while sym != nil: 
+    if sym.kind in filter: 
+      initCandidate(z, sym.typ)
+      z.calleeSym = sym
+      matches(c, n, z)
+      if z.state == csMatch: 
+        case x.state
+        of csEmpty, csNoMatch: 
+          x = z
+        of csMatch: 
+          var cmp = cmpCandidates(x, z)
+          if cmp < 0: 
+            x = z             # z is better than x
+          elif cmp == 0: 
+            y = z             # z is as good as x
+          else: 
+            nil
+    sym = nextOverloadIter(o, c, n.sons[0])
+  if x.state == csEmpty: 
+    # no overloaded proc found
+    # do not generate an error yet; the semantic checking will check for
+    # an overloaded () operator
+  elif (y.state == csMatch) and (cmpCandidates(x, y) == 0) and
+      not sameMethodDispatcher(x.calleeSym, y.calleeSym): 
+    if x.state != csMatch: 
+      InternalError(n.info, "x.state is not csMatch") #writeMatches(x);
+                                                      #writeMatches(y);
+    liMessage(n.Info, errGenerated, `%`(msgKindToString(errAmbiguousCallXYZ), [
+        getProcHeader(x.calleeSym), getProcHeader(y.calleeSym), 
+        x.calleeSym.Name.s]))
+  else: 
+    # only one valid interpretation found:
+    markUsed(n, x.calleeSym)
+    if x.calleeSym.ast == nil: 
+      internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
+    if x.calleeSym.ast.sons[genericParamsPos] != nil: 
+      # a generic proc!
+      x.calleeSym = generateInstance(c, x.calleeSym, x.bindings, n.info)
+      x.callee = x.calleeSym.typ
+    result = x.call
+    result.sons[0] = newSymNode(x.calleeSym)
+    result.typ = x.callee.sons[0]