fixes #797; generic procs can be used in places expecting matching concrete proc types
This commit is contained in:
parent
e8c87d070a
commit
a158053ae9
5 changed files with 80 additions and 29 deletions
|
|
@ -332,6 +332,7 @@ proc myOpen(module: PSym): PPassContext =
|
||||||
c.semOperand = semOperand
|
c.semOperand = semOperand
|
||||||
c.semConstBoolExpr = semConstBoolExpr
|
c.semConstBoolExpr = semConstBoolExpr
|
||||||
c.semOverloadedCall = semOverloadedCall
|
c.semOverloadedCall = semOverloadedCall
|
||||||
|
c.semGenerateInstance = generateInstance
|
||||||
c.semTypeNode = semTypeNode
|
c.semTypeNode = semTypeNode
|
||||||
pushProcCon(c, module)
|
pushProcCon(c, module)
|
||||||
pushOwner(c.module)
|
pushOwner(c.module)
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,8 @@ type
|
||||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||||
filter: TSymKinds): PNode {.nimcall.}
|
filter: TSymKinds): PNode {.nimcall.}
|
||||||
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.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
|
includedFiles*: TIntSet # used to detect recursive include files
|
||||||
userPragmas*: TStrTable
|
userPragmas*: TStrTable
|
||||||
evalContext*: PEvalContext
|
evalContext*: PEvalContext
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,14 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
if not isSymChoice(op):
|
if not isSymChoice(op):
|
||||||
let status = checkConvertible(c, result.typ, op.typ)
|
let status = checkConvertible(c, result.typ, op.typ)
|
||||||
case status
|
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:
|
of convNotNeedeed:
|
||||||
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||||
of convNotLegal:
|
of convNotLegal:
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ type
|
||||||
isSubtype,
|
isSubtype,
|
||||||
isSubrange, # subrange of the wanted type; no type conversion
|
isSubrange, # subrange of the wanted type; no type conversion
|
||||||
# but apart from that counts as ``isSubtype``
|
# 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,
|
isGeneric,
|
||||||
isFromIntLit, # conversion *from* int literal; proven safe
|
isFromIntLit, # conversion *from* int literal; proven safe
|
||||||
isEqual
|
isEqual
|
||||||
|
|
@ -338,10 +340,40 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
proc allowsNil(f: PType): TTypeRelation {.inline.} =
|
proc allowsNil(f: PType): TTypeRelation {.inline.} =
|
||||||
result = if tfNotNil notin f.flags: isSubtype else: isNone
|
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.} =
|
||||||
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
||||||
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
|
case a.kind
|
||||||
of tyProc:
|
of tyProc:
|
||||||
if sonsLen(f) != sonsLen(a): return
|
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
|
result = isEqual # start with maximum; also correct for no
|
||||||
# params at all
|
# params at all
|
||||||
for i in countup(1, sonsLen(f)-1):
|
for i in countup(1, sonsLen(f)-1):
|
||||||
var m = typeRel(c, f.sons[i], a.sons[i])
|
procParamTypeRel(c, f.sons[i], a.sons[i], result)
|
||||||
if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]):
|
|
||||||
return isNone
|
|
||||||
else: result = minRel(m, result)
|
|
||||||
if f.sons[0] != nil:
|
if f.sons[0] != nil:
|
||||||
if a.sons[0] != nil:
|
if a.sons[0] != nil:
|
||||||
var m = typeRel(c, f.sons[0], a.sons[0])
|
procParamTypeRel(c, f.sons[0], a.sons[0], result)
|
||||||
# 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)
|
|
||||||
else:
|
else:
|
||||||
return isNone
|
return isNone
|
||||||
elif a.sons[0] != nil:
|
elif a.sons[0] != nil:
|
||||||
|
|
@ -376,7 +400,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
elif f.callConv != a.callConv:
|
elif f.callConv != a.callConv:
|
||||||
# valid to pass a 'nimcall' thingie to 'closure':
|
# valid to pass a 'nimcall' thingie to 'closure':
|
||||||
if f.callConv == ccClosure and a.callConv == ccDefault:
|
if f.callConv == ccClosure and a.callConv == ccDefault:
|
||||||
result = isConvertible
|
result = if result != isInferred: isConvertible
|
||||||
|
else: isInferredConvertible
|
||||||
else:
|
else:
|
||||||
return isNone
|
return isNone
|
||||||
when useEffectSystem:
|
when useEffectSystem:
|
||||||
|
|
@ -402,18 +427,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||||
|
|
||||||
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
ff, a: PType): TTypeRelation =
|
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 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)
|
openScope(c)
|
||||||
inc c.inTypeClass
|
inc c.inTypeClass
|
||||||
|
|
||||||
|
|
@ -462,7 +477,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
return isGeneric
|
return isGeneric
|
||||||
# put(m.bindings, f, a)
|
|
||||||
|
|
||||||
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
# typeRel can be used to establish various relationships between types:
|
# typeRel can be used to establish various relationships between types:
|
||||||
|
|
@ -1022,6 +1036,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
inc(m.subtypeMatches)
|
inc(m.subtypeMatches)
|
||||||
#result = copyTree(arg)
|
#result = copyTree(arg)
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
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:
|
of isGeneric:
|
||||||
inc(m.genericMatches)
|
inc(m.genericMatches)
|
||||||
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
|
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
|
||||||
|
|
|
||||||
20
tests/generics/tinferredgenericprocs.nim
Normal file
20
tests/generics/tinferredgenericprocs.nim
Normal file
|
|
@ -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
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue