pickBestCandidate: pre-calculate candidates when symbol table modified

This commit is contained in:
Matthew Baulch 2016-09-03 20:20:48 +10:00
commit 3fef725d92

View file

@ -34,55 +34,85 @@ proc sameMethodDispatcher(a, b: PSym): bool =
proc determineType(c: PContext, s: PSym) 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, proc pickBestCandidate(c: PContext, headSymbol: PNode,
n, orig: PNode, n, orig: PNode,
initialBinding: PNode, initialBinding: PNode,
filter: TSymKinds, filter: TSymKinds,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors) = errors: var CandidateErrors) =
while true: var o: TOverloadIter
block pickAttempt: var sym = initOverloadIter(o, c, headSymbol)
var o: TOverloadIter var scope = o.lastOverloadScope
var sym = initOverloadIter(o, c, headSymbol) # Thanks to the lazy semchecking for operands, we need to check whether
# Thanks to the lazy semchecking for operands, we need to check whether # 'initCandidate' modifies the symbol table (via semExpr).
# 'initCandidate' modifies the symbol table (via semExpr). # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
# This can occur in cases like 'init(a, 1, (var b = new(Type2); b))' let counterInitial = c.currentScope.symbols.counter
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 # Initialise 'best' and 'alt' with the first available symbol
while sym != nil: initCandidate(c, best, sym, initialBinding, scope)
if sym.kind in filter: initCandidate(c, alt, sym, initialBinding, scope)
initCandidate(c, best, sym, initialBinding, o.lastOverloadScope) best.state = csNoMatch
initCandidate(c, alt, sym, initialBinding, o.lastOverloadScope) break
best.state = csNoMatch else:
break sym = nextOverloadIter(o, c, headSymbol)
else: scope = o.lastOverloadScope
sym = nextOverloadIter(o, c, headSymbol) var z: TCandidate
var z: TCandidate while sym != nil:
while sym != nil: if sym.kind notin filter:
if sym.kind notin filter: sym = nextOverloadIter(o, c, headSymbol)
sym = nextOverloadIter(o, c, headSymbol) scope = o.lastOverloadScope
continue continue
determineType(c, sym) determineType(c, sym)
initCandidate(c, z, sym, initialBinding, o.lastOverloadScope) initCandidate(c, z, sym, initialBinding, scope)
if c.currentScope.symbols.counter != counterInitial: break pickAttempt if c.currentScope.symbols.counter == counterInitial or syms != nil:
matches(c, n, orig, z) matches(c, n, orig, z)
if errors != nil: if errors != nil:
errors.safeAdd((sym, int z.mutabilityProblem)) errors.safeAdd((sym, int z.mutabilityProblem))
if z.errors != nil: if z.errors != nil:
for err in z.errors: for err in z.errors:
errors.add(err) errors.add(err)
if z.state == csMatch: if z.state == csMatch:
# little hack so that iterators are preferred over everything else: # little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200) if sym.kind == skIterator: inc(z.exactMatches, 200)
case best.state case best.state
of csEmpty, csNoMatch: best = z of csEmpty, csNoMatch: best = z
of csMatch: of csMatch:
var cmp = cmpCandidates(best, z) var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far 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 elif cmp == 0: alt = z # x is as good as the best so far
else: discard else:
sym = nextOverloadIter(o, c, headSymbol) # Symbol table has been modified. Restart and pre-calculate all syms
break # pick attempt was successful # 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) = proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,