diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 031ab8d704..01f23850bd 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1376,7 +1376,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = getTemp(p, getSysType(tyInt), i) # our counter initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - if d.k == locNone: getTemp(p, a.t, d) + if d.k == locNone: getTemp(p, getSysType(tyBool), d) lineF(p, cpsStmts, lookupOpr[op], [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: diff --git a/compiler/sem.nim b/compiler/sem.nim index 00ac79716a..845d4ae710 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -332,6 +332,7 @@ proc myOpen(module: PSym): PPassContext = c.semOperand = semOperand c.semConstBoolExpr = semConstBoolExpr c.semOverloadedCall = semOverloadedCall + c.semGenerateInstance = generateInstance c.semTypeNode = semTypeNode pushProcCon(c, module) pushOwner(c.module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c9d95e1bf4..0bc52d6b75 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -81,6 +81,8 @@ type semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} + semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, + info: TLineInfo): PSym includedFiles*: TIntSet # used to detect recursive include files userPragmas*: TStrTable evalContext*: PEvalContext diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a8a16672d6..3fe1367ec8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -204,7 +204,14 @@ proc semConv(c: PContext, n: PNode): PNode = if not isSymChoice(op): let status = checkConvertible(c, result.typ, op.typ) case status - of convOK: discard + of convOK: + # handle SomeProcType(SomeGenericProc) + # XXX: This needs fixing. checkConvertible uses typeRel internally, but + # doesn't bother to perform the work done in paramTypeMatchAux/fitNode + # so we are redoing the typeRel work here. Why does semConv exist as a + # separate proc from fitNode? + if op.kind == nkSym and op.sym.isGenericRoutine: + result.sons[1] = fitNode(c, result.typ, result.sons[1]) of convNotNeedeed: message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 335ceafeb2..227228f6e0 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -51,6 +51,8 @@ type isSubtype, isSubrange, # subrange of the wanted type; no type conversion # but apart from that counts as ``isSubtype`` + isInferred, # generic proc was matched against a concrete type + isInferredConvertible, # same as above, but requiring proc CC conversion isGeneric, isFromIntLit, # conversion *from* int literal; proven safe isEqual @@ -338,10 +340,40 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc allowsNil(f: PType): TTypeRelation {.inline.} = result = if tfNotNil notin f.flags: isSubtype else: isNone -proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = - proc inconsistentVarTypes(f, a: PType): bool {.inline.} = - result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) +proc inconsistentVarTypes(f, a: PType): bool {.inline.} = + result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) +proc procParamTypeRel(c: var TCandidate, f, a: PType, + result: var TTypeRelation) = + var + m: TTypeRelation + f = f + + if a.isMetaType: + if f.isMetaType: + # we are matching a generic proc (as proc param) + # to another generic type appearing in the proc + # sigunature. there is a change that the target + # type is already fully-determined, so we are + # going to try resolve it + f = generateTypeInstance(c.c, c.bindings, c.call.info, f) + if f == nil or f.isMetaType: + # no luck resolving the type, so the inference fails + result = isNone + return + let reverseRel = typeRel(c, a, f) + if reverseRel == isGeneric: + m = isInferred + else: + m = typeRel(c, f, a) + + if m <= isSubtype or inconsistentVarTypes(f, a): + result = isNone + return + else: + result = minRel(m, result) + +proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: if sonsLen(f) != sonsLen(a): return @@ -350,18 +382,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = result = isEqual # start with maximum; also correct for no # params at all for i in countup(1, sonsLen(f)-1): - var m = typeRel(c, f.sons[i], a.sons[i]) - if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]): - return isNone - else: result = minRel(m, result) + procParamTypeRel(c, f.sons[i], a.sons[i], result) if f.sons[0] != nil: if a.sons[0] != nil: - var m = typeRel(c, f.sons[0], a.sons[0]) - # Subtype is sufficient for return types! - if m < isSubtype or inconsistentVarTypes(f.sons[0], a.sons[0]): - return isNone - elif m == isSubtype: result = isConvertible - else: result = minRel(m, result) + procParamTypeRel(c, f.sons[0], a.sons[0], result) else: return isNone elif a.sons[0] != nil: @@ -376,7 +400,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = elif f.callConv != a.callConv: # valid to pass a 'nimcall' thingie to 'closure': if f.callConv == ccClosure and a.callConv == ccDefault: - result = isConvertible + result = if result != isInferred: isConvertible + else: isInferredConvertible else: return isNone when useEffectSystem: @@ -402,18 +427,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc matchUserTypeClass*(c: PContext, m: var TCandidate, ff, a: PType): TTypeRelation = - #if f.n == nil: - # let r = typeRel(m, f, a) - # return if r == isGeneric: arg else: nil - var body = ff.skipTypes({tyUserTypeClassInst}) - # var prev = PType(idTableGet(m.bindings, f)) - # if prev != nil: - # if sameType(prev, a): return arg - # else: return nil - - # pushInfoContext(arg.info) openScope(c) inc c.inTypeClass @@ -462,7 +477,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, else: discard return isGeneric - # put(m.bindings, f, a) proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # typeRel can be used to establish various relationships between types: @@ -988,7 +1002,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to @@ -1022,6 +1036,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, inc(m.subtypeMatches) #result = copyTree(arg) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + of isInferred, isInferredConvertible: + var prc = if arg.kind in nkLambdaKinds: arg[0].sym + else: arg.sym + let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) + result = newSymNode(inferred, arg.info) + if r == isInferredConvertible: + result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: inc(m.genericMatches) if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}: @@ -1035,10 +1056,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result = argOrig else: result = copyTree(arg) - result.typ = getInstantiatedType(c, arg, m, f) + result.typ = getInstantiatedType(c, arg, m, f) # BUG: f may not be the right key! if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) # BUGFIX: use ``result.typ`` and not `f` here of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate diff --git a/compiler/vm.nim b/compiler/vm.nim index 10ac7aaafa..81e7120472 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -441,7 +441,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = decodeBImm(nkIntLit) #assert regs[rb].kind == nkBracket # also used by mNLen: - regs[ra].intVal = regs[rb].skipMeta.len - imm + regs[ra].intVal = regs[rb].skipMeta.safeLen - imm of opcLenStr: decodeBImm(nkIntLit) if regs[rb].kind == nkNilLit: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index d0e8dacf32..a6d044e24d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -216,10 +216,12 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = internalAssert tmp >= 0 result = TRegister(tmp) -proc clearDest(n: PNode; dest: var TDest) {.inline.} = +proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. # So we only set dest to -1 if 'void': - if n.typ.isNil or n.typ.kind == tyEmpty: dest = -1 + if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty): + c.freeTemp(dest) + dest = -1 proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n.sons[0].kind == nkSym and @@ -259,7 +261,7 @@ proc genWhile(c: PCtx; n: PNode) = proc genBlock(c: PCtx; n: PNode; dest: var TDest) = withBlock(n.sons[0].sym): c.gen(n.sons[1], dest) - clearDest(n, dest) + c.clearDest(n, dest) proc genBreak(c: PCtx; n: PNode) = let L1 = c.xjmp(n, opcJmp) @@ -297,14 +299,16 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) = else: c.gen(it.sons[0], tmp) elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false + c.clearDest(n, dest) c.gen(it.sons[1], dest) # then part if i < sonsLen(n)-1: endings.add(c.xjmp(it.sons[1], opcJmp, 0)) c.patch(elsePos) else: + c.clearDest(n, dest) c.gen(it.sons[0], dest) for endPos in endings: c.patch(endPos) - clearDest(n, dest) + c.clearDest(n, dest) proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a @@ -385,8 +389,8 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = if i < sonsLen(n)-1: endings.add(c.xjmp(it.lastSon, opcJmp, 0)) c.patch(elsePos) + c.clearDest(n, dest) for endPos in endings: c.patch(endPos) - clearDest(n, dest) proc genType(c: PCtx; typ: PType): int = for i, t in c.types: @@ -400,6 +404,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = var endings: seq[TPosition] = @[] let elsePos = c.xjmp(n, opcTry, 0) c.gen(n.sons[0], dest) + c.clearDest(n, dest) c.patch(elsePos) for i in 1 ..