diff options
-rw-r--r-- | compiler/sem.nim | 1 | ||||
-rw-r--r-- | compiler/semdata.nim | 2 | ||||
-rw-r--r-- | compiler/semexprs.nim | 9 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 77 | ||||
-rw-r--r-- | tests/generics/tinferredgenericprocs.nim | 20 |
5 files changed, 80 insertions, 29 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim index 00ac79716..845d4ae71 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 c9d95e1bf..0bc52d6b7 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 a8a16672d..3fe1367ec 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 335ceafeb..227228f6e 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/tests/generics/tinferredgenericprocs.nim b/tests/generics/tinferredgenericprocs.nim new file mode 100644 index 000000000..ac445fd32 --- /dev/null +++ b/tests/generics/tinferredgenericprocs.nim @@ -0,0 +1,20 @@ +discard """ + output: '''123 +1 +2 +3''' +""" + +# https://github.com/Araq/Nimrod/issues/797 +proc foo[T](s:T):string = $s + +type IntStringProc = proc(x: int): string + +var f1 = IntStringProc(foo) +var f2: proc(x: int): string = foo +var f3: IntStringProc = foo + +echo f1(1), f2(2), f3(3) + +for x in map([1,2,3], foo): echo x + |