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
parent e8c87d070a
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.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall
c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode
pushProcCon(c, module)
pushOwner(c.module)

View File

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

View File

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

View File

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

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