pickBestCandidate: pre-calculate candidates when symbol table modified

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

View File

@@ -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,