diff options
Diffstat (limited to 'compiler/sigmatch.nim')
-rw-r--r-- | compiler/sigmatch.nim | 217 |
1 files changed, 148 insertions, 69 deletions
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4661abda0..d6a0c6382 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -26,7 +26,7 @@ type sym*: PSym unmatchedVarParam*: int diagnostics*: seq[string] - + CandidateErrors* = seq[CandidateError] TCandidate* = object @@ -68,7 +68,7 @@ type # future. mutabilityProblem*: uint8 # tyVar mismatch inheritancePenalty: int # to prefer closest father object type - + TTypeRelation* = enum # order is important! isNone, isConvertible, isIntConv, @@ -177,6 +177,13 @@ proc sumGeneric(t: PType): int = tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody: t = t.lastSon inc result + of tyOr: + var maxBranch = 0 + for branch in t.sons: + let branchSum = branch.sumGeneric + if branchSum > maxBranch: maxBranch = branchSum + inc result, maxBranch + 1 + break of tyVar: t = t.sons[0] inc result @@ -185,8 +192,8 @@ proc sumGeneric(t: PType): int = t = t.lastSon if t.kind == tyEmpty: break inc result - of tyGenericInvocation, tyTuple, tyProc: - result += ord(t.kind == tyGenericInvocation) + of tyGenericInvocation, tyTuple, tyProc, tyAnd: + result += ord(t.kind in {tyGenericInvocation, tyAnd}) for i in 0 .. <t.len: if t.sons[i] != nil: result += t.sons[i].sumGeneric @@ -228,6 +235,15 @@ proc complexDisambiguation(a, b: PType): int = for i in 1 .. <b.len: y += b.sons[i].sumGeneric result = x - y +proc writeMatches*(c: TCandidate) = + echo "Candidate '", c.calleeSym.name.s, "' at ", c.calleeSym.info + echo " exact matches: ", c.exactMatches + echo " generic matches: ", c.genericMatches + echo " subtype matches: ", c.subtypeMatches + echo " intconv matches: ", c.intConvMatches + echo " conv matches: ", c.convMatches + echo " inheritance: ", c.inheritancePenalty + proc cmpCandidates*(a, b: TCandidate): int = result = a.exactMatches - b.exactMatches if result != 0: return @@ -248,14 +264,6 @@ proc cmpCandidates*(a, b: TCandidate): int = if result != 0: return result = a.calleeScope - b.calleeScope -proc writeMatches*(c: TCandidate) = - writeLine(stdout, "exact matches: " & $c.exactMatches) - writeLine(stdout, "generic matches: " & $c.genericMatches) - writeLine(stdout, "subtype matches: " & $c.subtypeMatches) - writeLine(stdout, "intconv matches: " & $c.intConvMatches) - writeLine(stdout, "conv matches: " & $c.convMatches) - writeLine(stdout, "inheritance: " & $c.inheritancePenalty) - proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = if arg.kind in nkSymChoices: result = typeToString(arg[0].typ, prefer) @@ -639,7 +647,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, makeTypeDesc(c, typ) typeParams.safeAdd((param, typ)) - + addDecl(c, param) for param in typeClass.n[0]: @@ -676,7 +684,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, flags: TExprFlags = {} collectDiagnostics = m.diagnostics != nil or sfExplain in typeClass.sym.flags - + if collectDiagnostics: oldWriteHook = writelnHook # XXX: we can't write to m.diagnostics directly, because @@ -688,13 +696,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, let msg = s.replace("Error:", errorPrefix) if oldWriteHook != nil: oldWriteHook msg diagnostics.add msg - + var checkedBody = c.semTryExpr(c, body.copyTree, flags) if collectDiagnostics: writelnHook = oldWriteHook for msg in diagnostics: m.diagnostics.safeAdd msg - + if checkedBody == nil: return nil # The inferrable type params have been identified during the semTryExpr above. @@ -739,14 +747,14 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, allowMetaTypes = allowUnresolved) result = c.c.semExpr(c.c, instantiated) -proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType = +proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = # This is a simple integer arithimetic equation solver, # capable of deriving the value of a static parameter in # expressions such as (N + 5) / 2 = rhs # # Preconditions: # - # * The input of this proc must be semantized + # * The input of this proc must be semantized # - all templates should be expanded # - aby constant folding possible should already be performed # @@ -754,64 +762,69 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType = # # Result: # - # The proc will return the inferred static type with the `n` field - # populated with the inferred value. - # - # `nil` will be returned if the inference was not possible + # The proc will return true if the static types was successfully + # inferred. The result will be bound to the original static type + # in the TCandidate. # if lhs.kind in nkCallKinds and lhs[0].kind == nkSym: case lhs[0].sym.magic of mUnaryLt: - return inferStaticParam(lhs[1], rhs + 1) + return inferStaticParam(c, lhs[1], rhs + 1) of mAddI, mAddU, mInc, mSucc: if lhs[1].kind == nkIntLit: - return inferStaticParam(lhs[2], rhs - lhs[1].intVal) + return inferStaticParam(c, lhs[2], rhs - lhs[1].intVal) elif lhs[2].kind == nkIntLit: - return inferStaticParam(lhs[1], rhs - lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs - lhs[2].intVal) + of mDec, mSubI, mSubU, mPred: if lhs[1].kind == nkIntLit: - return inferStaticParam(lhs[2], lhs[1].intVal - rhs) + return inferStaticParam(c, lhs[2], lhs[1].intVal - rhs) elif lhs[2].kind == nkIntLit: - return inferStaticParam(lhs[1], rhs + lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs + lhs[2].intVal) + of mMulI, mMulU: if lhs[1].kind == nkIntLit: if rhs mod lhs[1].intVal == 0: - return inferStaticParam(lhs[2], rhs div lhs[1].intVal) + return inferStaticParam(c, lhs[2], rhs div lhs[1].intVal) elif lhs[2].kind == nkIntLit: if rhs mod lhs[2].intVal == 0: - return inferStaticParam(lhs[1], rhs div lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs div lhs[2].intVal) + of mDivI, mDivU: if lhs[1].kind == nkIntLit: if lhs[1].intVal mod rhs == 0: - return inferStaticParam(lhs[2], lhs[1].intVal div rhs) + return inferStaticParam(c, lhs[2], lhs[1].intVal div rhs) elif lhs[2].kind == nkIntLit: - return inferStaticParam(lhs[1], lhs[2].intVal * rhs) - + return inferStaticParam(c, lhs[1], lhs[2].intVal * rhs) + of mShlI: if lhs[2].kind == nkIntLit: - return inferStaticParam(lhs[1], rhs shr lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs shr lhs[2].intVal) + of mShrI: if lhs[2].kind == nkIntLit: - return inferStaticParam(lhs[1], rhs shl lhs[2].intVal) - + return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal) + of mUnaryMinusI: - return inferStaticParam(lhs[1], -rhs) - + return inferStaticParam(c, lhs[1], -rhs) + of mUnaryPlusI, mToInt, mToBiggestInt: - return inferStaticParam(lhs[1], rhs) - + return inferStaticParam(c, lhs[1], rhs) + else: discard - + elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: - lhs.typ.n = newIntNode(nkIntLit, rhs) - return lhs.typ - - return nil + var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons) + inferred.n = newIntNode(nkIntLit, rhs) + put(c, lhs.typ, inferred) + if c.c.inTypeClass > 0: + # inside concepts, binding is currently done with + # direct mutation of the involved types: + lhs.typ.n = inferred.n + return true + + return false proc failureToInferStaticParam(n: PNode) = let staticParam = n.findUnresolvedStatic @@ -825,13 +838,10 @@ proc inferStaticsInRange(c: var TCandidate, allowUnresolved = true) let upperBound = tryResolvingStaticExpr(c, inferred.n[1], allowUnresolved = true) - - template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) = + template doInferStatic(e: PNode, r: BiggestInt) = var exp = e var rhs = r - var inferred = inferStaticParam(exp, rhs) - if inferred != nil: - put(c, inferred, inferred) + if inferStaticParam(c, exp, rhs): return isGeneric else: failureToInferStaticParam exp @@ -842,9 +852,9 @@ proc inferStaticsInRange(c: var TCandidate, return isGeneric else: return isNone - doInferStatic(c, upperBound, lengthOrd(concrete) + lowerBound.intVal - 1) + doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1) elif upperBound.kind == nkIntLit: - doInferStatic(c, lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) + doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete)) template subtypeCheck() = if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: @@ -1049,18 +1059,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}), a.sons[1].skipTypes({tyTypeDesc})) if result < isGeneric: return isNone - + if fRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, fRange, a) elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) - elif lengthOrd(fRange) != lengthOrd(a): - result = isNone + else: + if lengthOrd(fRange) != lengthOrd(aRange): + result = isNone else: discard of tyOpenArray, tyVarargs: # varargs[expr] is special too but handled earlier. So we only need to # handle varargs[stmt] which is the same as varargs[typed]: if f.kind == tyVarargs: + if tfVarargs in a.flags: + return typeRel(c, f.base, a.lastSon) if tfOldSchoolExprStmt in f.sons[0].flags: if f.sons[0].kind == tyExpr: return elif f.sons[0].kind == tyStmt: return @@ -1204,9 +1217,51 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyEmpty, tyVoid: if a.kind == f.kind: result = isEqual - of tyGenericInst, tyAlias: + of tyAlias: result = typeRel(c, lastSon(f), a) + of tyGenericInst: + var prev = PType(idTableGet(c.bindings, f)) + var f = if prev == nil: f else: prev + + let roota = a.skipGenericAlias + let rootf = f.skipGenericAlias + + var m = c + if a.kind == tyGenericInst: + if roota.base == rootf.base: + for i in 1 .. rootf.sonsLen-2: + let ff = rootf.sons[i] + let aa = roota.sons[i] + result = typeRel(c, ff, aa) + if result notin {isEqual, isGeneric}: return isNone + # if ff.kind == tyRange and result != isEqual: return isNone + + if prev == nil: put(c, f, a) + result = isGeneric + else: + let fKind = rootf.lastSon.kind + if fKind in {tyAnd, tyOr}: + result = typeRel(c, lastSon(f), a) + if result != isNone: put(c, f, a) + return + + var aAsObject = roota.lastSon + + if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind: + aAsObject = aAsObject.base + + if aAsObject.kind == tyObject: + let baseType = aAsObject.base + if baseType != nil: + c.inheritancePenalty += 1 + return typeRel(c, f, baseType) + + result = isNone + else: + result = typeRel(c, lastSon(f), a) + if result != isNone: put(c, f, a) + of tyGenericBody: considerPreviousT: if a.kind == tyGenericInst and a.sons[0] == f: @@ -1217,6 +1272,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericInvocation: var x = a.skipGenericAlias + # XXX: This is very hacky. It should be moved back into liftTypeParam + if x.kind in {tyGenericInst, tyArray} and + c.calleeSym != nil and + c.calleeSym.kind == skProc: + let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f) + return typeRel(c, inst, a) + var depth = 0 if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody: #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") @@ -1333,13 +1395,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if f.isResolvedUserTypeClass: result = typeRel(c, f.lastSon, a) else: - var matched = matchUserTypeClass(c.c, c, f, aOrig) - if matched != nil: - bindConcreteTypeToUserTypeClass(matched, a) - put(c, f, matched) - result = isGeneric - else: - result = isNone + considerPreviousT: + var matched = matchUserTypeClass(c.c, c, f, aOrig) + if matched != nil: + bindConcreteTypeToUserTypeClass(matched, a) + if doBind: put(c, f, matched) + result = isGeneric + else: + result = isNone of tyCompositeTypeClass: considerPreviousT: @@ -1357,6 +1420,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if result != isNone: put(c, f, a) result = isGeneric + of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) if x == nil: @@ -1376,16 +1440,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = internalAssert a.sons != nil and a.sons.len > 0 c.typedescMatched = true var aa = a - while aa.kind in {tyTypeDesc, tyGenericParam} and - aa.len > 0: + while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: aa = lastSon(aa) + if aa.kind == tyGenericParam: + return isGeneric result = typeRel(c, f.base, aa) if result > isGeneric: result = isGeneric else: result = isNone else: if f.sonsLen > 0 and f.sons[0].kind != tyNone: - result = typeRel(c, f.lastSon, a) + result = typeRel(c, f.lastSon, a, false) if doBind and result notin {isNone, isGeneric}: let concrete = concreteType(c, a) if concrete == nil: return isNone @@ -1590,6 +1655,10 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = of isEqual: inc(m.exactMatches) of isNone: discard +template matchesVoidProc(t: PType): bool = + (t.kind == tyProc and t.len == 1 and t.sons[0] == nil) or + (t.kind == tyBuiltInTypeClass and t.sons[0].kind == tyProc) + proc paramTypesMatchAux(m: var TCandidate, f, a: PType, argSemantized, argOrig: PNode): PNode = var @@ -1726,6 +1795,14 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, inc(m.genericMatches) m.fauxMatch = a.kind return arg + elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList: + # lift do blocks without params to lambdas + let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {}) + if f.kind == tyBuiltInTypeClass: + inc m.genericMatches + put(m, f, lifted.typ) + inc m.convMatches + return implicitConv(nkHiddenStdConv, f, lifted, m, c) result = userConvMatch(c, m, f, a, arg) # check for a base type match, which supports varargs[T] without [] # constructor in a call: @@ -2008,6 +2085,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, #assert(container == nil) if container.isNil: container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg)) + container.typ.flags.incl tfVarargs else: incrIndexType(container.typ) addSon(container, arg) @@ -2086,6 +2164,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") return nil var f = dc.typ.sons[col] + if op == attachedDeepCopy: if f.kind in {tyRef, tyPtr}: f = f.lastSon else: |