summary refs log blame commit diff stats
path: root/compiler/sigmatch.nim
blob: 953dcfa7472c339bfb1c3ab4aca92510ca1924bf (plain) (tree)
1
2
3
4
5
6
7
8
9


                               
                                         



                                                   
 

                                                              
 
       
                                                                             

                                                                      
 
    
                          
                               
 
                                 

                                                                      


                                                                      


                                              
                                                          
                                            
                                                  

                                                                            
                                                        

                                                                   
                                                                   
  
                                                  






                                                                              
           


                                 
                                                                     






                      

                         
                            
                          
 
                                                        



                             

                                                       
 

                                                                     

                                 
                             
                         
                                                    



                                                             
                                                      
 












                                                       
                                            
                                          
                        
                                              
                        
                                              
                        
                                              
                        
                                        


                                                     


                                                      
 
                                    




                                                          
 
                                            


                                                                             



                                               
                                       
                         


                                                


                                 

                                             
                     
                      
                                             

                                

                                         
                     


                                                                           
                                          
  

                                                           



                                      


                                                                              



                                                  
                                               




                                                     
                         


                                                            


                                                                    

                       
       






                                                             

                                                                           

                                                    
                        






                                                                        
                         











                                                                          
                                                    


                       



                                             
                                                                 

                         
                                        
           
                           
               




                                                


                  




                                                  
                                                              
                 

                    
                                
                    
                                        
                                              
                                     
                                

                                            

                                                                         




                                                                           
 


                                                            
                                                                 



                                                                      

                                       




                                                                   
                                              




                                                                      
                                                

                                                                       
                       


                                                   
                     
                          
                   
                                                                  
                   
                                                                            
                                                      
                   

                                                          




                                                             
                     

                                                   
                                

           



                                                                    
                                                              



                      
                                
                                        
                                           
                                     
                                    
                                         
                                   

             

                                                                      
                     
                                         
                                                                   

                        
                                           

                                               


                                                          
                                                  


                                                            
                                                            
                                                           




                                                              



                                                
            

                                                              
                            


                                             








                                                        
                                               

                                                             
             
                            
               
                              
                                           



                                                                             
                                                        



                                                                             
                                                        



                                                                             
                                                        
                              
             
                
               

                                                                            
                          
           
                                                 
                                              
                                  
             

                        
                                                       
                                       
                                            
                                                          
           

                                         

                                                    

                                                





                                          
                                                                           




                                                                             
                                                 




                                       
                                           
                                                 
                                  
             

               
             
                                           
                                                 
                                  
             
            
                                 


                                  
                                  


                                                        
             


                                 
                                  
             
               

                                                     
                                  
                                  
                                       
             





                                                                        
             


                                          
                                      
                    
                       
                                            
                         
                              
                                           
                                     


                                                                           



                                                              
                                           
                                             
                                                   
                                                                   
                        
         
                                       
                          
                                                                 
                                            
                                                                      
                                                                         
                                                     
                                       
                                 
                                            
                
                                      
                             
                                         


                           
                                      
                           
                        
                                  
                        
         
                                                  




                            

                                                   

                     
                    
                      

                    
                                            
  
                                             


                           





                                                                 


                                                  



                                                                        






                                                    
                                                               
                               







                                                                










                                                                    
                                    


                                         
                                                          


                                   
                                                             
                   
 

                                                                 

                                    





                                                                 
                                             


                                                            


                                                          
 
                                                                     
                                                     




                      
                                         
















                                                                          
                        
  



                                                                  
               
                                                                          
                                          




                                                                  


                          
               
                         
                                                                        





                                                   






                                                                       




                                                                      


                          
                                                    
                                                                    

                                                                           

                           

                          
                                            
                                                                       
                            


                                                
           








                                                                
 

                                                                   
                                               
                                                         




                                                                            
                           





                              
                 
                                           
                                                                              
                           
                                              






                                 
                                         




                                                 









                                                              

                                                                              
 



                                                       

                                                          








                                                                    


                                                      
                                   


                                          

                                                    
                  
                                      


                                            
                                                             






                                                                     
                                                  
                                 
                                                                      


                             

                                                                         
                     
                           

                                        


















                                                                                
                           
                                 

                                                                    










                                                                      
                
                                       
                                                    
                                   
                                                                        


                               

                                                                  
                      
                             

                           







                                                                            
                                

          
 
                                                                     
                          
                           
                                    
 
                                                                
                           
                                    


                                                  
                                
                                       
                                                    

                                        
                                                     
                                                                  




                                                       
           


                                                                 
 








                                                                    
               
#
#
#           The Nimrod Compiler
#        (c) Copyright 2013 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.

import 
  intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
  magicsys, condsyms, idents, lexer, options, parampatterns, strutils,
  docgen

type
  TCandidateState* = enum 
    csEmpty, csMatch, csNoMatch

  TCandidate* {.final.} = object 
    exactMatches*: int       # also misused to prefer iters over procs
    genericMatches: int      # also misused to prefer constraints
    subtypeMatches: int
    intConvMatches: int      # conversions to int are not as expensive
    convMatches: int
    state*: TCandidateState
    callee*: PType           # may not be nil!
    calleeSym*: PSym         # may be nil
    calleeScope: int         # may be -1 for unknown scope
    call*: PNode             # modified call
    bindings*: TIdTable      # maps types to types
    baseTypeMatch: bool      # needed for conversions from T to openarray[T]
                             # for example
    proxyMatch*: bool        # to prevent instantiations
    genericConverter*: bool  # true if a generic converter needs to
                             # be instantiated
    inheritancePenalty: int  # to prefer closest father object type
  
  TTypeRelation* = enum      # order is important!
    isNone, isConvertible,
    isIntConv,
    isSubtype,
    isSubrange,              # subrange of the wanted type; no type conversion
                             # but apart from that counts as ``isSubtype``
    isGeneric,
    isFromIntLit,            # conversion *from* int literal; proven safe
    isEqual
    
proc markUsed*(n: PNode, s: PSym)

proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = 
  c.exactMatches = 0
  c.subtypeMatches = 0
  c.convMatches = 0
  c.intConvMatches = 0
  c.genericMatches = 0
  c.state = csEmpty
  c.callee = callee
  c.call = nil
  c.baseTypeMatch = false
  c.genericConverter = false
  c.inheritancePenalty = 0

proc initCandidate*(c: var TCandidate, callee: PType) = 
  initCandidateAux(c, callee)
  c.calleeSym = nil
  initIdTable(c.bindings)

proc put(t: var TIdTable, key, val: PType) {.inline.} =
  IdTablePut(t, key, val)

proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, 
                    calleeScope = -1) =
  initCandidateAux(c, callee.typ)
  c.calleeSym = callee
  c.calleeScope = calleeScope
  initIdTable(c.bindings)
  if binding != nil and callee.kind in RoutineKinds:
    var typeParams = callee.ast[genericParamsPos]
    for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
      var formalTypeParam = typeParams.sons[i-1].typ
      #debug(formalTypeParam)
      put(c.bindings, formalTypeParam, binding[i].typ)

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
  if result != 0: return
  if (a.calleeScope != -1) and (b.calleeScope != -1):
    result = a.calleeScope - b.calleeScope
  if result != 0: return
  # the other way round because of other semantics:
  result = b.inheritancePenalty - a.inheritancePenalty

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 NotFoundError*(c: PContext, n: PNode) =
  # Gives a detailed error message; this is separated from semOverloadedCall,
  # as semOverlodedCall is already pretty slow (and we need this information
  # only in case of an error).
  if c.InCompilesContext > 0: 
    # fail fast:
    GlobalError(n.info, errTypeMismatch, "")
  var result = msgKindToString(errTypeMismatch)
  for i in countup(1, sonsLen(n) - 1): 
    #debug(n.sons[i].typ)
    if n.sons[i].kind == nkExprEqExpr: 
      add(result, renderTree(n.sons[i].sons[0]))
      add(result, ": ")
    let nt = n.sons[i].typ
    if nt.kind == tyError: return
    add(result, typeToString(nt))
    if i != sonsLen(n) - 1: add(result, ", ")
  add(result, ')')
  var candidates = ""
  var o: TOverloadIter
  var sym = initOverloadIter(o, c, n.sons[0])
  while sym != nil:
    if sym.kind in RoutineKinds:
      add(candidates, getProcHeader(sym))
      add(candidates, "\n")
      #debug(sym.typ)
    sym = nextOverloadIter(o, c, n.sons[0])
  if candidates != "": 
    add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
  LocalError(n.Info, errGenerated, result)
  
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation
proc concreteType(c: TCandidate, t: PType): PType = 
  case t.kind
  of tyArrayConstr: 
    # make it an array
    result = newType(tyArray, t.owner)
    addSonSkipIntLit(result, t.sons[0]) # XXX: t.owner is wrong for ID!
    addSonSkipIntLit(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(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:
    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: 
    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, 
                                   tyUInt..tyUInt64} and
        isIntLit(ab) and ab.n.intVal >= firstOrd(f) and
                         ab.n.intVal <= lastOrd(f):
      # integer literal in the proper range; we want ``i16 + 4`` to stay an
      # ``int16`` operation so we declare the ``4`` pseudo-equal to int16
      result = isFromIntLit
    elif f.kind == tyInt and k in {tyInt8..tyInt32}:
      result = isIntConv
    elif k >= min and k <= max: 
      result = isConvertible
    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):
      result = isConvertible
    else: result = isNone
    #elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv
    #elif f.kind == tyUInt and k in {tyUInt..tyUInt32}: result = isIntConv

proc isConvertibleToRange(f, a: PType): bool =
  # be less picky for tyRange, as that it is used for array indexing:
  if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and
     a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}:
    result = true
  elif f.kind in {tyFloat..tyFloat128} and
       a.kind in {tyFloat..tyFloat128}:
    result = true

proc handleFloatRange(f, a: PType): TTypeRelation = 
  if a.kind == f.kind: 
    result = isEqual
  else: 
    let ab = skipTypes(a, {tyRange})
    var k = ab.kind
    if k == f.kind: result = isSubrange
    elif isIntLit(ab): result = isConvertible
    elif k >= tyFloat and k <= tyFloat128: result = isConvertible
    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): 
    assert t.kind == tyObject
    t = t.sons[0]
    if t == nil: break
    t = skipTypes(t, {tyGenericInst})
    inc depth
  if t != nil:
    result = depth

proc minRel(a, b: TTypeRelation): TTypeRelation = 
  if a <= b: result = a
  else: result = b
  
proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation =
  result = isNone
  if sameType(f, a):
    result = isEqual
  elif sonsLen(a) == sonsLen(f):
    result = isEqual
    for i in countup(0, sonsLen(f) - 1):
      var m = typeRel(c, 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")
        elif a.n.sons[i].kind != nkSym: InternalError(a.n.info, "tupleRel")
        else:
          var x = f.n.sons[i].sym
          var y = a.n.sons[i].sym
          if x.name.id != y.name.id: return isNone

proc allowsNil(f: PType): TTypeRelation {.inline.} =
  result = if tfNotNil notin f.flags: isSubtype else: isNone

proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
  proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
    result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)

  case a.kind
  of tyProc:
    if sonsLen(f) != sonsLen(a): return
    # 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):
      var m = typeRel(c, f.sons[i], a.sons[i])
      if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]):
        return isNone
      else: result = minRel(m, result)
    if f.sons[0] != nil:
      if a.sons[0] != nil:
        var m = typeRel(c, f.sons[0], a.sons[0])
        # Subtype is sufficient for return types!
        if m < isSubtype or inconsistentVarTypes(f.sons[0], a.sons[0]):
          return isNone
        elif m == isSubtype: result = isConvertible
        else: result = minRel(m, result)
      else:
        return isNone
    elif a.sons[0] != nil:
      return isNone
    if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
      return isNone
    elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
      # noSideEffect implies ``tfThread``! XXX really?
      return isNone
    elif f.flags * {tfIterator} != a.flags * {tfIterator}:
      return isNone
    elif f.callconv != a.callconv:
      # valid to pass a 'nimcall' thingie to 'closure':
      if f.callconv == ccClosure and a.callconv == ccDefault:
        result = isConvertible
      else:
        return isNone
    when useEffectSystem:
      if not compatibleEffects(f, a): return isNone
  of tyNil: result = f.allowsNil
  else: nil

proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
  result = if matchTypeClass(c.bindings, f, a): isGeneric
           else: isNone

proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation = 
  # is a subtype of f?
  result = isNone
  assert(f != nil)
  assert(a != nil)
  if a.kind == tyGenericInst and
      skipTypes(f, {tyVar}).kind notin {
        tyGenericBody, tyGenericInvokation,
        tyGenericParam, tyTypeClass}:
    return typeRel(c, f, lastSon(a))
  if a.kind == tyVar and f.kind != tyVar:
    return typeRel(c, f, a.sons[0])
  case f.kind
  of tyEnum: 
    if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
    elif sameEnumTypes(f, skipTypes(a, {tyRange})): 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(c, base(f), base(a))
      # bugfix: accept integer conversions here
      #if result < isGeneric: result = isNone
    elif skipTypes(f, {tyRange}).kind == a.kind:
      result = isIntConv
    elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
      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, tyInt8, tyInt32)
  of tyInt64:    result = handleRange(f, a, tyInt, tyInt64)
  of tyUInt:     result = handleRange(f, a, tyUInt8, tyUInt32)
  of tyUInt8:    result = handleRange(f, a, tyUInt8, tyUInt8)
  of tyUInt16:   result = handleRange(f, a, tyUInt8, tyUInt16)
  of tyUInt32:   result = handleRange(f, a, tyUInt8, tyUInt32)
  of tyUInt64:   result = handleRange(f, a, tyUInt, tyUInt64)
  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(c, base(f), base(a))
    else: result = typeRel(c, base(f), a)
  of tyArray, tyArrayConstr:
    # tyArrayConstr cannot happen really, but
    # we wanna be safe here
    case a.kind
    of tyArray, tyArrayConstr:
      var fRange = f.sons[0]
      if fRange.kind == tyGenericParam:
        var prev = PType(idTableGet(c.bindings, fRange))
        if prev == nil:
          put(c.bindings, fRange, a.sons[0])
          fRange = a
        else:
          fRange = prev
      result = typeRel(c, f.sons[1], a.sons[1])
      if result < isGeneric: result = isNone
      elif lengthOrd(fRange) != lengthOrd(a): result = isNone
    else: nil
  of tyOpenArray, tyVarargs:
    case a.Kind
    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): 
        result = isSubtype    # [] is allowed here
      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): 
        result = isSubtype
      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): 
        result = isConvertible
      elif typeRel(c, base(f), a.sons[0]) >= isGeneric: 
        result = isConvertible
    else: nil
  of tySequence:
    case a.Kind
    of tySequence:
      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
        result = isSubtype
      else:
        result = typeRel(c, f.sons[0], a.sons[0])
        if result < isGeneric: result = isNone
    of tyNil: result = f.allowsNil
    else: nil
  of tyOrdinal:
    if isOrdinalType(a):
      var x = if a.kind == tyOrdinal: a.sons[0] else: a
      result = typeRel(c, 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(c, f, a)
  of tyObject:
    if a.kind == tyObject:
      if sameObjectTypes(f, a): result = isEqual
      else:
        var depth = isObjectSubtype(a, f)
        if depth > 0:
          inc(c.inheritancePenalty, depth)
          result = isSubtype
  of tyDistinct:
    if (a.kind == tyDistinct) and sameDistinctTypes(f, a): 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(c, f.sons[0], a.sons[0])
        if result <= isConvertible: 
          result = isNone     # BUGFIX!
  of tyPtr: 
    case a.kind
    of tyPtr: 
      result = typeRel(c, base(f), base(a))
      if result <= isConvertible: result = isNone
    of tyNil: result = f.allowsNil
    else: nil
  of tyRef: 
    case a.kind
    of tyRef:
      result = typeRel(c, base(f), base(a))
      if result <= isConvertible: result = isNone
    of tyNil: result = f.allowsNil
    else: nil
  of tyProc:
    result = procTypeRel(c, f, a)
  of tyPointer: 
    case a.kind
    of tyPointer: result = isEqual
    of tyNil: result = f.allowsNil
    of tyProc:
      if a.callConv != ccClosure: result = isConvertible
    of tyPtr, tyCString: result = isConvertible
    else: nil
  of tyString: 
    case a.kind
    of tyString: result = isEqual
    of tyNil: result = f.allowsNil
    else: nil
  of tyCString:
    # conversion from string to cstring is automatic:
    case a.Kind
    of tyCString: result = isEqual
    of tyNil: result = f.allowsNil
    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(c, lastSon(f), a)
  of tyGenericBody: 
    let ff = lastSon(f)
    if ff != nil: result = typeRel(c, ff, a)
  of tyGenericInvokation:
    var x = a.skipGenericAlias
    assert(f.sons[0].kind == tyGenericBody)
    if x.kind == tyGenericInvokation:
      #InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation")
      # simply no match for now:
      nil
    elif x.kind == tyGenericInst and 
          (f.sons[0].containerID == x.sons[0].containerID) and
          (sonsLen(x) - 1 == sonsLen(f)): 
      assert(x.sons[0].kind == tyGenericBody)
      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 
      result = isGeneric
    else:
      result = typeRel(c, f.sons[0], x)
      if result != isNone:
        # 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}:
            InternalError("wrong instantiated type!")
          put(c.bindings, f.sons[i], x)
  of tyGenericParam, tyTypeClass:
    var x = PType(idTableGet(c.bindings, f))
    if x == nil:
      result = matchTypeClass(c, f, a)
      if result == isGeneric:
        var concrete = concreteType(c, a)
        if concrete == nil:
          result = isNone
        else:
          put(c.bindings, f, concrete)
    elif a.kind == tyEmpty:
      result = isGeneric
    elif x.kind == tyGenericParam:
      result = isGeneric
    else:
      result = typeRel(c, x, a) # check if it fits
  of tyTypeDesc:
    if a.kind == tyTypeDesc:
      if f.sonsLen == 0:
        result = isGeneric
      else:
        result = matchTypeClass(c, f, a.sons[0])
      if result == isGeneric: put(c.bindings, f, a)
    else:
      result = isNone
  of tyExpr, tyStmt:
    result = isGeneric
  of tyProxy:
    result = isEqual
  else: internalError("typeRel: " & $f.kind)
  
proc cmpTypes*(f, a: PType): TTypeRelation = 
  var c: TCandidate
  InitCandidate(c, f)
  result = typeRel(c, 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")
    result = errorType(c)
  
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:
      result.typ = getInstantiatedType(c, arg, m, f)
    else:
      result.typ = errorType(c)
  else:
    result.typ = f
  if result.typ == nil: InternalError(arg.info, "implicitConv")
  addSon(result, ast.emptyNode)
  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]
    # for generic type converters we need to check 'src <- a' before
    # 'f <- dest' in order to not break the unification:
    # 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}: 
      markUsed(arg, c.converters[i])
      var s = newSymNode(c.converters[i])
      s.typ = c.converters[i].typ
      s.info = arg.info
      result = newNodeIT(nkHiddenCallConv, arg.info, dest)
      addSon(result, s)
      addSon(result, copyTree(arg))
      inc(m.convMatches)
      m.genericConverter = srca == isGeneric or destIsGeneric
      return result

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
  var call = newNodeI(nkCall, arg.info)
  call.add(f.n.copyTree)
  call.add(arg.copyTree)
  result = c.semOverloadedCall(c, call, call, RoutineKinds)
  if result != nil:
    # resulting type must be consistent with the other arguments:
    var r = typeRel(m, f.sons[0], result.typ)
    if r < isGeneric: return nil
    if result.kind == nkCall: result.kind = nkHiddenCallConv
    inc(m.convMatches)
    if r == isGeneric:
      result.typ = getInstantiatedType(c, arg, m, base(f))
    m.baseTypeMatch = true

proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 
                        arg, argOrig: PNode): PNode =
  var r: TTypeRelation
  if f.kind == tyExpr:
    if f.sonsLen == 0:
      r = isGeneric
    else:
      let match = matchTypeClass(m, f, a)
      if match != isGeneric: r = isNone
      else:
        # XXX: Ideally, this should happen much earlier somewhere near 
        # semOpAux, but to do that, we need to be able to query the 
        # overload set to determine whether compile-time value is expected
        # for the param before entering the full-blown sigmatch algorithm.
        # This is related to the immediate pragma since querying the
        # overload set could help there too.
        var evaluated = c.semConstExpr(c, arg)
        if evaluated != nil:
          r = isGeneric
          arg.typ = newTypeS(tyExpr, c)
          arg.typ.n = evaluated
        
    if r == isGeneric:
      put(m.bindings, f, arg.typ)
  else:
    r = typeRel(m, f, a)
  
  case r
  of isConvertible: 
    inc(m.convMatches)
    result = implicitConv(nkHiddenStdConv, f, copyTree(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: 
    inc(m.subtypeMatches)
    result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
  of isSubrange:
    inc(m.subtypeMatches)
    result = copyTree(arg)
  of isGeneric:
    inc(m.genericMatches)
    if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
      if f.kind == tyStmt and argOrig.kind == nkDo:
        result = argOrig[bodyPos]
      elif f.kind == tyTypeDesc:
        result = arg
      else:
        result = argOrig
    else:
      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}:
        result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) 
        # BUGFIX: use ``result.typ`` and not `f` here
  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: 
    inc(m.exactMatches)
    result = copyTree(arg)
    if skipTypes(f, abstractVar).kind in {tyTuple}: 
      result = implicitConv(nkHiddenStdConv, f, copyTree(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:
      inc(m.genericMatches)
      m.proxyMatch = true
      return copyTree(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:
      if f.n != nil:
        result = localConvMatch(c, m, f, a, arg)
      else:
        r = typeRel(m, 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, argOrig: PNode): PNode =
  if arg == nil or arg.kind notin nkSymChoices:
    result = ParamTypesMatchAux(c, m, f, a, arg, argOrig)
  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.
    var x, y, z: TCandidate
    initCandidate(x, m.callee)
    initCandidate(y, m.callee)
    initCandidate(z, m.callee)
    x.calleeSym = m.calleeSym
    y.calleeSym = m.calleeSym
    z.calleeSym = m.calleeSym
    var best = -1
    for i in countup(0, sonsLen(arg) - 1): 
      if arg.sons[i].sym.kind in {skProc, skIterator, skMethod, skConverter}: 
        copyCandidate(z, m)
        var r = typeRel(z, f, arg.sons[i].typ)
        if r != isNone: 
          case x.state
          of csEmpty, csNoMatch: 
            x = z
            best = i
            x.state = csMatch
          of csMatch: 
            var 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
      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],
                                  argOrig)

proc setSon(father: PNode, at: int, son: PNode) = 
  if sonsLen(father) <= at: setlen(father.sons, at + 1)
  father.sons[at] = son

proc matchesAux(c: PContext, n, nOrig: PNode,
                m: var TCandidate, marker: var TIntSet) = 
  template checkConstraint(n: expr) {.immediate, dirty.} =
    if not formal.constraint.isNil:
      if matchNodeKinds(formal.constraint, n):
        # better match over other routines with no such restriction:
        inc(m.genericMatches, 100)
      else:
        m.state = csNoMatch
        return
  
  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(n.kind, 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 container: PNode = nil # constructed container
  var formal: PSym = nil
  while a < n.len:
    if n.sons[a].kind == nkExprEqExpr:
      # named param
      # check if m.callee has such a param:
      if n.sons[a].sons[0].kind != nkIdent: 
        LocalError(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 ContainsOrIncl(marker, formal.position): 
        # already in namedParams:
        LocalError(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], nOrig.sons[a].sons[1])
      if arg == nil: 
        m.state = csNoMatch
        return
      checkConstraint(n.sons[a].sons[1])
      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], nOrig.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")
          return
        formal = m.callee.n.sons[f].sym
        if ContainsOrIncl(marker, formal.position): 
          # already in namedParams:
          LocalError(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], nOrig.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)
      checkConstraint(n.sons[a])
    inc(a)
    inc(f)

proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
  # for 'suggest' support:
  var marker = initIntSet()
  matchesAux(c, n, nOrig, m, marker)

proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
  var marker = initIntSet()
  matchesAux(c, n, nOrig, m, marker)
  if m.state == csNoMatch: return
  # check that every formal parameter got a value:
  var f = 1
  while f < sonsLen(m.callee.n):
    var formal = m.callee.n.sons[f].sym
    if not ContainsOrIncl(marker, formal.position): 
      if formal.ast == nil:
        if formal.typ.kind == tyVarargs:
          var container = newNodeI(nkBracket, n.info)
          addSon(m.call, 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))
    inc(f)

proc argtypeMatches*(c: PContext, f, a: PType): bool = 
  var m: TCandidate
  initCandidate(m, f)
  let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil)
  #instantiateGenericConverters(c, res, m)
  # XXX this is used by patterns.nim too; I think it's better to not
  # instantiate generic converters for that
  result = res != nil

include suggest