diff options
Diffstat (limited to 'compiler/semcall.nim')
-rw-r--r-- | compiler/semcall.nim | 169 |
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)) |