summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMatthew Baulch <baulch.matt@gmail.com>2016-09-03 20:20:48 +1000
committerMatthew Baulch <baulch.matt@gmail.com>2016-09-03 20:20:48 +1000
commit3fef725d920f87f2f8bf8de80d746c8a8b568055 (patch)
tree6cb12c8bbd05b367781c7ca46c3d7182819db1ff
parent7e643d73788fd0799cc970601bc75592e9610039 (diff)
downloadNim-3fef725d920f87f2f8bf8de80d746c8a8b568055.tar.gz
pickBestCandidate: pre-calculate candidates when symbol table modified
-rw-r--r--compiler/semcall.nim114
1 files changed, 72 insertions, 42 deletions
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index b3fc020b1..5f7e5084b 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -34,55 +34,85 @@ proc sameMethodDispatcher(a, b: PSym): bool =
 
 proc determineType(c: PContext, s: PSym)
 
+proc initCandidateSymbols(c: PContext, headSymbol: PNode,
+                       initialBinding: PNode,
+                       filter: TSymKinds,
+                       best, alt: var TCandidate,
+                       o: var TOverloadIter): seq[tuple[s: PSym, scope: int]] =
+  result = @[]
+  var symx = initOverloadIter(o, c, headSymbol)
+  while symx != nil:
+    if symx.kind in filter:
+      result.add((symx, o.lastOverloadScope))
+      symx = nextOverloadIter(o, c, headSymbol)
+  if result.len > 0:
+    initCandidate(c, best, result[0].s, initialBinding, result[0].scope)
+    initCandidate(c, alt, result[0].s, initialBinding, result[0].scope)
+    best.state = csNoMatch
+
 proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        n, orig: PNode,
                        initialBinding: PNode,
                        filter: TSymKinds,
                        best, alt: var TCandidate,
                        errors: var CandidateErrors) =
-  while true:
-    block pickAttempt:
-      var o: TOverloadIter
-      var sym = initOverloadIter(o, c, headSymbol)
-      # Thanks to the lazy semchecking for operands, we need to check whether
-      # 'initCandidate' modifies the symbol table (via semExpr).
-      # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
-      let counterInitial = c.currentScope.symbols.counter
+  var o: TOverloadIter
+  var sym = initOverloadIter(o, c, headSymbol)
+  var scope = o.lastOverloadScope
+  # Thanks to the lazy semchecking for operands, we need to check whether
+  # 'initCandidate' modifies the symbol table (via semExpr).
+  # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
+  let counterInitial = c.currentScope.symbols.counter
+  var syms: seq[tuple[s: PSym, scope: int]]
+  var nextSymIndex = 0
+  while sym != nil:
+    if sym.kind in filter:
       # Initialise 'best' and 'alt' with the first available symbol
-      while sym != nil:
-        if sym.kind in filter:
-          initCandidate(c, best, sym, initialBinding, o.lastOverloadScope)
-          initCandidate(c, alt, sym, initialBinding, o.lastOverloadScope)
-          best.state = csNoMatch
-          break
-        else:
-          sym = nextOverloadIter(o, c, headSymbol)
-      var z: TCandidate
-      while sym != nil:
-        if sym.kind notin filter:
-          sym = nextOverloadIter(o, c, headSymbol)
-          continue
-        determineType(c, sym)
-        initCandidate(c, z, sym, initialBinding, o.lastOverloadScope)
-        if c.currentScope.symbols.counter != counterInitial: break pickAttempt
-        matches(c, n, orig, z)
-        if errors != nil:
-          errors.safeAdd((sym, int z.mutabilityProblem))
-          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 == skIterator: 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)
-    break # pick attempt was successful
+      initCandidate(c, best, sym, initialBinding, scope)
+      initCandidate(c, alt, sym, initialBinding, scope)
+      best.state = csNoMatch
+      break
+    else:
+      sym = nextOverloadIter(o, c, headSymbol)
+      scope = o.lastOverloadScope
+  var z: TCandidate
+  while sym != nil:
+    if sym.kind notin filter:
+      sym = nextOverloadIter(o, c, headSymbol)
+      scope = o.lastOverloadScope
+      continue
+    determineType(c, sym)
+    initCandidate(c, z, sym, initialBinding, scope)
+    if c.currentScope.symbols.counter == counterInitial or syms != nil:
+      matches(c, n, orig, z)
+      if errors != nil:
+        errors.safeAdd((sym, int z.mutabilityProblem))
+        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 == skIterator: 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:
+      # Symbol table has been modified. Restart and pre-calculate all syms
+      # before any further candidate init and compare. SLOW, but rare case.
+      syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o)
+    if syms == nil:
+      sym = nextOverloadIter(o, c, headSymbol)
+      scope = o.lastOverloadScope
+    elif nextSymIndex < syms.len:
+      # rare case: retrieve the next pre-calculated symbol
+      sym = syms[nextSymIndex].s
+      scope = syms[nextSymIndex].scope
+      nextSymIndex += 1
+    else:
+      break
 
 proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,