summary refs log tree commit diff stats
path: root/compiler/semcall.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semcall.nim')
-rw-r--r--compiler/semcall.nim169
1 files changed, 104 insertions, 65 deletions
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 3971b8ff5..c48e761e3 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -7,16 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements semantic checking for calls. 
+## This module implements semantic checking for calls.
 # included from sem.nim
 
 proc sameMethodDispatcher(a, b: PSym): bool =
   result = false
-  if a.kind == skMethod and b.kind == skMethod: 
+  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:
-      if aa.sym == bb.sym: 
+      if aa.sym == bb.sym:
         result = true
     else:
       discard
@@ -31,7 +31,7 @@ proc sameMethodDispatcher(a, b: PSym): bool =
       # to avoid subtle problems, the call remains ambiguous and needs to
       # be disambiguated by the programmer; this way the right generic is
       # instantiated.
-  
+
 proc determineType(c: PContext, s: PSym)
 
 proc pickBestCandidate(c: PContext, headSymbol: PNode,
@@ -41,62 +41,80 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        best, alt: var TCandidate,
                        errors: var CandidateErrors) =
   var o: TOverloadIter
-  var sym = initOverloadIter(o, c, headSymbol)
-  var symScope = o.lastOverloadScope
+  # thanks to the lazy semchecking for operands, we need to iterate over the
+  # symbol table *before* any call to 'initCandidate' which might invoke
+  # semExpr which might modify the symbol table in cases like
+  # 'init(a, 1, (var b = new(Type2); b))'.
+  var symx = initOverloadIter(o, c, headSymbol)
+  let symScope = o.lastOverloadScope
+
+  var syms: seq[tuple[a: PSym, b: int]] = @[]
+  while symx != nil:
+    if symx.kind in filter: syms.add((symx, o.lastOverloadScope))
+    symx = nextOverloadIter(o, c, headSymbol)
+  if syms.len == 0: return
 
   var z: TCandidate
-  
-  if sym == nil: return
-  initCandidate(c, best, sym, initialBinding, symScope)
-  initCandidate(c, alt, sym, initialBinding, symScope)
+  initCandidate(c, best, syms[0][0], initialBinding, symScope)
+  initCandidate(c, alt, syms[0][0], initialBinding, symScope)
   best.state = csNoMatch
-  
-  while sym != nil:
-    if sym.kind in filter:
-      determineType(c, sym)
-      initCandidate(c, z, sym, initialBinding, o.lastOverloadScope)
-      z.calleeSym = sym
-      matches(c, n, orig, z)
-      if errors != nil:
-        errors.safeAdd(sym)
-        if z.errors != nil:
-          for err in z.errors:
-            errors.add(err)
-      if z.state == csMatch:
-        # little hack so that iterators are preferred over everything else:
-        if sym.kind in skIterators: inc(z.exactMatches, 200)
-        case best.state
-        of csEmpty, csNoMatch: best = z
-        of csMatch:
-          var cmp = cmpCandidates(best, z)
-          if cmp < 0: best = z   # x is better than the best so far
-          elif cmp == 0: alt = z # x is as good as the best so far
-          else: discard
-    sym = nextOverloadIter(o, c, headSymbol)
+
+  for i in 0 .. <syms.len:
+    let sym = syms[i][0]
+    determineType(c, sym)
+    initCandidate(c, z, sym, initialBinding, syms[i][1])
+    z.calleeSym = sym
+
+    #if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
+    #  gDebug = true
+    matches(c, n, orig, z)
+    if errors != nil:
+      errors.safeAdd(sym)
+      if z.errors != nil:
+        for err in z.errors:
+          errors.add(err)
+    if z.state == csMatch:
+      # little hack so that iterators are preferred over everything else:
+      if sym.kind in skIterators: inc(z.exactMatches, 200)
+      case best.state
+      of csEmpty, csNoMatch: best = z
+      of csMatch:
+        var cmp = cmpCandidates(best, z)
+        if cmp < 0: best = z   # x is better than the best so far
+        elif cmp == 0: alt = z # x is as good as the best so far
+        else: discard
+      #if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
+      #  echo "Matches ", n.info, " ", typeToString(sym.typ)
+      #  debug sym
+      #  writeMatches(z)
+      #  for i in 1 .. <len(z.call):
+      #    z.call[i].typ.debug
+      #  quit 1
 
 proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # 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: 
+  if c.inCompilesContext > 0:
     # fail fast:
     globalError(n.info, errTypeMismatch, "")
-  if errors.len == 0:
+  if errors.isNil or errors.len == 0:
     localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
+    return
 
-  # to avoid confusing errors like: 
+  # to avoid confusing errors like:
   #   got (SslPtr, SocketHandle)
-  #   but expected one of: 
+  #   but expected one of:
   #   openssl.SSL_set_fd(ssl: SslPtr, fd: SocketHandle): cint
   # we do a pre-analysis. If all types produce the same string, we will add
   # module information.
   let proto = describeArgs(c, n, 1, preferName)
-  
+
   var prefer = preferName
   for err in errors:
     var errProto = ""
     let n = err.typ.n
-    for i in countup(1, n.len - 1): 
+    for i in countup(1, n.len - 1):
       var p = n.sons[i]
       if p.kind == nkSym:
         add(errProto, typeToString(p.sym.typ, preferName))
@@ -123,7 +141,8 @@ proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
       for s in scope.usingSyms: usedSyms.safeAdd(s)
 
 proc resolveOverloads(c: PContext, n, orig: PNode,
-                      filter: TSymKinds): TCandidate =
+                      filter: TSymKinds;
+                      errors: var CandidateErrors): TCandidate =
   var initialBinding: PNode
   var alt: TCandidate
   var f = n.sons[0]
@@ -134,7 +153,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
   else:
     initialBinding = nil
 
-  var errors: CandidateErrors
   var usedSyms: seq[PNode]
 
   template pickBest(headSymbol: expr) =
@@ -148,9 +166,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
     n.sons.insert(hiddenArg, 1)
     orig.sons.insert(hiddenArg, 1)
-    
+
     pickBest(f)
- 
+
     if result.state != csMatch:
       n.sons.delete(1)
       orig.sons.delete(1)
@@ -168,7 +186,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       # we are going to try multiple variants
       n.sons[0..1] = [nil, n[1], calleeName]
       orig.sons[0..1] = [nil, orig[1], calleeName]
-      
+
       template tryOp(x) =
         let op = newIdentNode(getIdent(x), n.info)
         n.sons[0] = op
@@ -177,18 +195,19 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
       if nfExplicitCall in n.flags:
         tryOp ".()"
-   
+
       if result.state in {csEmpty, csNoMatch}:
         tryOp "."
 
     elif nfDotSetter in n.flags:
       internalAssert f.kind == nkIdent and n.sonsLen == 3
-      let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
+      let calleeName = newStrNode(nkStrLit,
+        f.ident.s[0..f.ident.s.len-2]).withInfo(n.info)
       let callOp = newIdentNode(getIdent".=", n.info)
       n.sons[0..1] = [callOp, n[1], calleeName]
       orig.sons[0..1] = [callOp, orig[1], calleeName]
       pickBest(callOp)
-   
+
     if overloadsState == csEmpty and result.state == csEmpty:
       localError(n.info, errUndeclaredIdentifier, considerQuotedIdent(f).s)
       return
@@ -204,7 +223,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
         errors = @[]
         pickBest(f)
-        notFoundError(c, n, errors)
+        #notFoundError(c, n, errors)
 
       return
 
@@ -213,7 +232,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
     internalAssert result.state == csMatch
     #writeMatches(result)
     #writeMatches(alt)
-    if c.inCompilesContext > 0: 
+    if c.inCompilesContext > 0:
       # quick error message for performance of 'compiles' built-in:
       globalError(n.info, errGenerated, "ambiguous call")
     elif gErrorCounter == 0:
@@ -243,7 +262,7 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
     for i in 1 .. <n.len:
       instGenericConvertersArg(c, n.sons[i], x)
 
-proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = 
+proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
   var m: TCandidate
   initCandidate(c, m, f)
   result = paramTypesMatch(m, f, a, arg, nil)
@@ -260,7 +279,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
     instGenericConvertersArg(c, result, m)
   if result != nil:
     # This almost exactly replicates the steps taken by the compiler during
-    # param matching. It performs an embarassing ammount of back-and-forth
+    # param matching. It performs an embarrassing amount of back-and-forth
     # type jugling, but it's the price to pay for consistency and correctness
     result.typ = generateTypeInstance(c, m.bindings, arg.info,
                                       formal.skipTypes({tyCompositeTypeClass}))
@@ -277,27 +296,47 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
   styleCheckUse(n.sons[0].info, finalCallee)
   if finalCallee.ast == nil:
     internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
+  if x.hasFauxMatch:
+    result = x.call
+    result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
+    if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
+      result.typ = newTypeS(x.fauxMatch, c)
+    return
   if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
-    # a generic proc!
-    if not x.proxyMatch:
-      finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
-    else:
-      result = x.call
-      result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
-      result.typ = finalCallee.typ.sons[0]
-      if containsGenericType(result.typ): result.typ = errorType(c)
-      return
+    finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
   result = x.call
   instGenericConvertersSons(c, result, x)
   result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
   result.typ = finalCallee.typ.sons[0]
 
+proc canDeref(n: PNode): bool {.inline.} =
+  result = n.len >= 2 and (let t = n[1].typ;
+    t != nil and t.skipTypes({tyGenericInst}).kind in {tyPtr, tyRef})
+
+proc tryDeref(n: PNode): PNode =
+  result = newNodeI(nkHiddenDeref, n.info)
+  result.typ = n.typ.skipTypes(abstractInst).sons[0]
+  result.addSon(n)
+
 proc semOverloadedCall(c: PContext, n, nOrig: PNode,
                        filter: TSymKinds): PNode =
-  var r = resolveOverloads(c, n, nOrig, filter)
+  var errors: CandidateErrors
+
+  var r = resolveOverloads(c, n, nOrig, filter, errors)
   if r.state == csMatch: result = semResolvedCall(c, n, r)
+  elif experimentalMode(c) and canDeref(n):
+    # try to deref the first argument and then try overloading resolution again:
+    n.sons[1] = n.sons[1].tryDeref
+    var r = resolveOverloads(c, n, nOrig, filter, errors)
+    if r.state == csMatch: result = semResolvedCall(c, n, r)
+    else:
+      # get rid of the deref again for a better error message:
+      n.sons[1] = n.sons[1].sons[0]
+      notFoundError(c, n, errors)
+  else:
+    notFoundError(c, n, errors)
   # else: result = errorNode(c, n)
-    
+
 proc explicitGenericInstError(n: PNode): PNode =
   localError(n.info, errCannotInstantiateX, renderTree(n))
   result = n
@@ -310,7 +349,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
   styleCheckUse(n.info, s)
   result = newSymNode(newInst, n.info)
 
-proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = 
+proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   assert n.kind == nkBracketExpr
   for i in 1..sonsLen(n)-1:
     n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
@@ -330,11 +369,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
     # XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
     # It's good enough for now.
     result = newNodeI(a.kind, n.info)
-    for i in countup(0, len(a)-1): 
+    for i in countup(0, len(a)-1):
       var candidate = a.sons[i].sym
       if candidate.kind in {skProc, skMethod, skConverter,
                             skIterator, skClosureIterator}:
-        # it suffices that the candidate has the proper number of generic 
+        # it suffices that the candidate has the proper number of generic
         # type parameters:
         if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
           result.add(explicitGenericSym(c, n, candidate))