fixes #797; generic procs can be used in places expecting matching concrete proc types

This commit is contained in:
Zahary Karadjov 2014-02-11 01:14:57 +02:00
commit a158053ae9
5 changed files with 80 additions and 29 deletions

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -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}:

View 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