mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-01 10:52:14 +00:00
overloading resolution finally takes inheritance depth into account
This commit is contained in:
@@ -32,6 +32,7 @@ type
|
||||
bindings*: TIdTable # maps types to types
|
||||
baseTypeMatch: bool # needed for conversions from T to openarray[T]
|
||||
# for example
|
||||
inheritancePenalty: int # to prefer closest father object type
|
||||
|
||||
TTypeRelation* = enum # order is important!
|
||||
isNone, isConvertible,
|
||||
@@ -53,6 +54,7 @@ proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
|
||||
c.callee = callee
|
||||
c.call = nil
|
||||
c.baseTypeMatch = false
|
||||
c.inheritancePenalty = 0
|
||||
|
||||
proc initCandidate*(c: var TCandidate, callee: PType) =
|
||||
initCandidateAux(c, callee)
|
||||
@@ -100,6 +102,9 @@ proc cmpCandidates*(a, b: TCandidate): int =
|
||||
if result != 0: return
|
||||
if (a.calleeScope != -1) and (b.calleeScope != -1):
|
||||
result = a.calleeScope - b.calleeScope
|
||||
if result != 0: return
|
||||
# the other way round because of other semantics:
|
||||
result = b.inheritancePenalty - a.inheritancePenalty
|
||||
|
||||
proc writeMatches*(c: TCandidate) =
|
||||
Writeln(stdout, "exact matches: " & $c.exactMatches)
|
||||
@@ -133,8 +138,8 @@ proc getNotFoundError*(c: PContext, n: PNode): string =
|
||||
if candidates != "":
|
||||
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
||||
|
||||
proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation
|
||||
proc concreteType(mapping: TIdTable, t: PType): PType =
|
||||
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation
|
||||
proc concreteType(c: TCandidate, t: PType): PType =
|
||||
case t.kind
|
||||
of tyArrayConstr:
|
||||
# make it an array
|
||||
@@ -146,7 +151,7 @@ proc concreteType(mapping: TIdTable, t: PType): PType =
|
||||
of tyGenericParam:
|
||||
result = t
|
||||
while true:
|
||||
result = PType(idTableGet(mapping, t))
|
||||
result = PType(idTableGet(c.bindings, t))
|
||||
if result == nil:
|
||||
break # it's ok, no match
|
||||
# example code that triggers it:
|
||||
@@ -204,28 +209,31 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
elif k >= tyFloat and k <= tyFloat128: result = isConvertible
|
||||
else: result = isNone
|
||||
|
||||
proc isObjectSubtype(a, f: PType): bool =
|
||||
proc isObjectSubtype(a, f: PType): int =
|
||||
var t = a
|
||||
assert t.kind == tyObject
|
||||
var depth = 0
|
||||
while t != nil and not sameObjectTypes(f, t):
|
||||
assert t.kind == tyObject
|
||||
t = t.sons[0]
|
||||
if t == nil: break
|
||||
t = skipTypes(t, {tyGenericInst})
|
||||
result = t != nil
|
||||
inc depth
|
||||
if t != nil:
|
||||
result = depth
|
||||
|
||||
proc minRel(a, b: TTypeRelation): TTypeRelation =
|
||||
if a <= b: result = a
|
||||
else: result = b
|
||||
|
||||
proc tupleRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
result = isNone
|
||||
if sameType(f, a):
|
||||
result = isEqual
|
||||
elif sonsLen(a) == sonsLen(f):
|
||||
result = isEqual
|
||||
for i in countup(0, sonsLen(f) - 1):
|
||||
var m = typeRel(mapping, f.sons[i], a.sons[i])
|
||||
var m = typeRel(c, f.sons[i], a.sons[i])
|
||||
if m < isSubtype: return isNone
|
||||
result = minRel(result, m)
|
||||
if f.n != nil and a.n != nil:
|
||||
@@ -237,7 +245,7 @@ proc tupleRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
var y = a.n.sons[i].sym
|
||||
if x.name.id != y.name.id: return isNone
|
||||
|
||||
proc matchTypeClass(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
for i in countup(0, f.sonsLen - 1):
|
||||
let son = f.sons[i]
|
||||
var match = son.kind == skipTypes(a, {tyRange}).kind
|
||||
@@ -247,9 +255,9 @@ proc matchTypeClass(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
of tyGenericBody:
|
||||
if a.kind == tyGenericInst and a.sons[0] == son:
|
||||
match = true
|
||||
put(mapping, f, a)
|
||||
put(c.bindings, f, a)
|
||||
of tyTypeClass:
|
||||
match = matchTypeClass(mapping, son, a) == isGeneric
|
||||
match = matchTypeClass(c, son, a) == isGeneric
|
||||
else: nil
|
||||
|
||||
if tfAny in f.flags:
|
||||
@@ -263,7 +271,7 @@ proc matchTypeClass(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
# or none of them matched.
|
||||
result = if tfAny in f.flags: isNone else: isGeneric
|
||||
|
||||
proc procTypeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
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)
|
||||
|
||||
@@ -276,13 +284,13 @@ proc procTypeRel(mapping: var TIdTable, 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(mapping, f.sons[i], a.sons[i])
|
||||
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)
|
||||
if f.sons[0] != nil:
|
||||
if a.sons[0] != nil:
|
||||
var m = typeRel(mapping, f.sons[0], a.sons[0])
|
||||
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]):
|
||||
result = isNone
|
||||
@@ -305,7 +313,7 @@ proc procTypeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
result = isNone
|
||||
else: nil
|
||||
|
||||
proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||
# is a subtype of f?
|
||||
result = isNone
|
||||
assert(f != nil)
|
||||
@@ -314,9 +322,9 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
skipTypes(f, {tyVar}).kind notin {
|
||||
tyGenericBody, tyGenericInvokation,
|
||||
tyGenericParam, tyTypeClass}:
|
||||
return typeRel(mapping, f, lastSon(a))
|
||||
return typeRel(c, f, lastSon(a))
|
||||
if a.kind == tyVar and f.kind != tyVar:
|
||||
return typeRel(mapping, f, a.sons[0])
|
||||
return typeRel(c, f, a.sons[0])
|
||||
case f.kind
|
||||
of tyEnum:
|
||||
if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual
|
||||
@@ -326,7 +334,7 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
|
||||
of tyRange:
|
||||
if a.kind == f.kind:
|
||||
result = typeRel(mapping, base(f), base(a))
|
||||
result = typeRel(c, base(f), base(a))
|
||||
# bugfix: accept integer conversions here
|
||||
#if result < isGeneric: result = isNone
|
||||
elif skipTypes(f, {tyRange}).kind == a.kind:
|
||||
@@ -348,45 +356,45 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
of tyFloat64: result = handleFloatRange(f, a)
|
||||
of tyFloat128: result = handleFloatRange(f, a)
|
||||
of tyVar:
|
||||
if a.kind == f.kind: result = typeRel(mapping, base(f), base(a))
|
||||
else: result = typeRel(mapping, base(f), a)
|
||||
if a.kind == f.kind: result = typeRel(c, base(f), base(a))
|
||||
else: result = typeRel(c, base(f), a)
|
||||
of tyArray, tyArrayConstr:
|
||||
# tyArrayConstr cannot happen really, but
|
||||
# we wanna be safe here
|
||||
case a.kind
|
||||
of tyArray:
|
||||
result = minRel(typeRel(mapping, f.sons[0], a.sons[0]),
|
||||
typeRel(mapping, f.sons[1], a.sons[1]))
|
||||
result = minRel(typeRel(c, f.sons[0], a.sons[0]),
|
||||
typeRel(c, f.sons[1], a.sons[1]))
|
||||
if result < isGeneric: result = isNone
|
||||
of tyArrayConstr:
|
||||
result = typeRel(mapping, f.sons[1], a.sons[1])
|
||||
result = typeRel(c, f.sons[1], a.sons[1])
|
||||
if result < isGeneric:
|
||||
result = isNone
|
||||
else:
|
||||
if (result != isGeneric) and (lengthOrd(f) != lengthOrd(a)):
|
||||
result = isNone
|
||||
elif f.sons[0].kind in GenericTypes:
|
||||
result = minRel(result, typeRel(mapping, f.sons[0], a.sons[0]))
|
||||
result = minRel(result, typeRel(c, f.sons[0], a.sons[0]))
|
||||
else: nil
|
||||
of tyOpenArray:
|
||||
case a.Kind
|
||||
of tyOpenArray:
|
||||
result = typeRel(mapping, base(f), base(a))
|
||||
result = typeRel(c, base(f), base(a))
|
||||
if result < isGeneric: result = isNone
|
||||
of tyArrayConstr:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
|
||||
result = isSubtype # [] is allowed here
|
||||
elif typeRel(mapping, base(f), a.sons[1]) >= isGeneric:
|
||||
elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
|
||||
result = isSubtype
|
||||
of tyArray:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
|
||||
result = isSubtype
|
||||
elif typeRel(mapping, base(f), a.sons[1]) >= isGeneric:
|
||||
elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
|
||||
result = isConvertible
|
||||
of tySequence:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||
result = isConvertible
|
||||
elif typeRel(mapping, base(f), a.sons[0]) >= isGeneric:
|
||||
elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
|
||||
result = isConvertible
|
||||
else: nil
|
||||
of tySequence:
|
||||
@@ -397,49 +405,53 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||
result = isSubtype
|
||||
else:
|
||||
result = typeRel(mapping, f.sons[0], a.sons[0])
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
if result < isGeneric: result = isNone
|
||||
else: nil
|
||||
of tyOrdinal:
|
||||
if isOrdinalType(a):
|
||||
var x = if a.kind == tyOrdinal: a.sons[0] else: a
|
||||
result = typeRel(mapping, f.sons[0], x)
|
||||
result = typeRel(c, f.sons[0], x)
|
||||
if result < isGeneric: result = isNone
|
||||
of tyForward: InternalError("forward type in typeRel()")
|
||||
of tyNil:
|
||||
if a.kind == f.kind: result = isEqual
|
||||
of tyTuple:
|
||||
if a.kind == tyTuple: result = tupleRel(mapping, f, a)
|
||||
of tyObject:
|
||||
if a.kind == tyTuple: result = tupleRel(c, f, a)
|
||||
of tyObject:
|
||||
if a.kind == tyObject:
|
||||
if sameObjectTypes(f, a): result = isEqual
|
||||
elif isObjectSubtype(a, f): result = isSubtype
|
||||
of tyDistinct:
|
||||
else:
|
||||
var depth = isObjectSubtype(a, f)
|
||||
if depth > 0:
|
||||
inc(c.inheritancePenalty, depth)
|
||||
result = isSubtype
|
||||
of tyDistinct:
|
||||
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
|
||||
of tySet:
|
||||
if a.kind == tySet:
|
||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||
result = isSubtype
|
||||
else:
|
||||
result = typeRel(mapping, f.sons[0], a.sons[0])
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
if result <= isConvertible:
|
||||
result = isNone # BUGFIX!
|
||||
of tyPtr:
|
||||
case a.kind
|
||||
of tyPtr:
|
||||
result = typeRel(mapping, base(f), base(a))
|
||||
result = typeRel(c, base(f), base(a))
|
||||
if result <= isConvertible: result = isNone
|
||||
of tyNil: result = isSubtype
|
||||
else: nil
|
||||
of tyRef:
|
||||
case a.kind
|
||||
of tyRef:
|
||||
result = typeRel(mapping, base(f), base(a))
|
||||
result = typeRel(c, base(f), base(a))
|
||||
if result <= isConvertible: result = isNone
|
||||
of tyNil: result = isSubtype
|
||||
else: nil
|
||||
of tyProc:
|
||||
result = procTypeRel(mapping, f, a)
|
||||
result = procTypeRel(c, f, a)
|
||||
of tyPointer:
|
||||
case a.kind
|
||||
of tyPointer: result = isEqual
|
||||
@@ -468,10 +480,10 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
of tyEmpty:
|
||||
if a.kind == tyEmpty: result = isEqual
|
||||
of tyGenericInst:
|
||||
result = typeRel(mapping, lastSon(f), a)
|
||||
result = typeRel(c, lastSon(f), a)
|
||||
of tyGenericBody:
|
||||
let ff = lastSon(f)
|
||||
if ff != nil: result = typeRel(mapping, ff, a)
|
||||
if ff != nil: result = typeRel(c, ff, a)
|
||||
of tyGenericInvokation:
|
||||
assert(f.sons[0].kind == tyGenericBody)
|
||||
if a.kind == tyGenericInvokation:
|
||||
@@ -485,40 +497,40 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
for i in countup(1, sonsLen(f) - 1):
|
||||
if a.sons[i].kind == tyGenericParam:
|
||||
InternalError("wrong instantiated type!")
|
||||
if typeRel(mapping, f.sons[i], a.sons[i]) <= isSubtype: return
|
||||
if typeRel(c, f.sons[i], a.sons[i]) <= isSubtype: return
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(mapping, f.sons[0], a)
|
||||
result = typeRel(c, f.sons[0], a)
|
||||
if result != isNone:
|
||||
# we steal the generic parameters from the tyGenericBody:
|
||||
for i in countup(1, sonsLen(f) - 1):
|
||||
var x = PType(idTableGet(mapping, f.sons[0].sons[i - 1]))
|
||||
var x = PType(idTableGet(c.bindings, f.sons[0].sons[i - 1]))
|
||||
if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}:
|
||||
InternalError("wrong instantiated type!")
|
||||
put(mapping, f.sons[i], x)
|
||||
put(c.bindings, f.sons[i], x)
|
||||
of tyGenericParam, tyTypeClass:
|
||||
var x = PType(idTableGet(mapping, f))
|
||||
var x = PType(idTableGet(c.bindings, f))
|
||||
if x == nil:
|
||||
result = matchTypeClass(mapping, f, a)
|
||||
result = matchTypeClass(c, f, a)
|
||||
if result == isGeneric:
|
||||
var concrete = concreteType(mapping, a)
|
||||
var concrete = concreteType(c, a)
|
||||
if concrete == nil:
|
||||
result = isNone
|
||||
else:
|
||||
put(mapping, f, concrete)
|
||||
put(c.bindings, f, concrete)
|
||||
elif a.kind == tyEmpty:
|
||||
result = isGeneric
|
||||
elif x.kind == tyGenericParam:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(mapping, x, a) # check if it fits
|
||||
result = typeRel(c, x, a) # check if it fits
|
||||
of tyTypeDesc:
|
||||
if a.kind == tyTypeDesc:
|
||||
if f.sonsLen == 0:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = matchTypeClass(mapping, f, a.sons[0])
|
||||
if result == isGeneric: put(mapping, f, a)
|
||||
result = matchTypeClass(c, f, a.sons[0])
|
||||
if result == isGeneric: put(c.bindings, f, a)
|
||||
else:
|
||||
result = isNone
|
||||
of tyExpr, tyStmt:
|
||||
@@ -526,9 +538,9 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
else: internalError("typeRel(" & $f.kind & ')')
|
||||
|
||||
proc cmpTypes*(f, a: PType): TTypeRelation =
|
||||
var mapping: TIdTable
|
||||
InitIdTable(mapping)
|
||||
result = typeRel(mapping, f, a)
|
||||
var c: TCandidate
|
||||
InitCandidate(c, f)
|
||||
result = typeRel(c, f, a)
|
||||
|
||||
proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
|
||||
f: PType): PType =
|
||||
@@ -552,8 +564,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
for i in countup(0, len(c.converters) - 1):
|
||||
var src = c.converters[i].typ.sons[1]
|
||||
var dest = c.converters[i].typ.sons[0]
|
||||
if (typeRel(m.bindings, f, dest) == isEqual) and
|
||||
(typeRel(m.bindings, src, a) == isEqual):
|
||||
if (typeRel(m, f, dest) == isEqual) and
|
||||
(typeRel(m, src, a) == isEqual):
|
||||
markUsed(arg, c.converters[i])
|
||||
var s = newSymNode(c.converters[i])
|
||||
s.typ = c.converters[i].typ
|
||||
@@ -562,7 +574,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
addSon(result, s)
|
||||
addSon(result, copyTree(arg))
|
||||
inc(m.convMatches)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||
@@ -572,7 +584,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||
if f.sonsLen == 0:
|
||||
r = isGeneric
|
||||
else:
|
||||
let match = matchTypeClass(m.bindings, f, a)
|
||||
let match = matchTypeClass(m, f, a)
|
||||
if match != isGeneric: r = isNone
|
||||
else:
|
||||
# XXX: Ideally, this should happen much earlier somewhere near
|
||||
@@ -590,7 +602,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||
if r == isGeneric:
|
||||
put(m.bindings, f, arg.typ)
|
||||
else:
|
||||
r = typeRel(m.bindings, f, a)
|
||||
r = typeRel(m, f, a)
|
||||
|
||||
case r
|
||||
of isConvertible:
|
||||
@@ -633,13 +645,13 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||
# check for a base type match, which supports openarray[T] without []
|
||||
# constructor in a call:
|
||||
if result == nil and f.kind == tyOpenArray:
|
||||
r = typeRel(m.bindings, base(f), a)
|
||||
r = typeRel(m, base(f), a)
|
||||
if r >= isGeneric:
|
||||
inc(m.convMatches)
|
||||
result = copyTree(arg)
|
||||
if r == isGeneric: result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||
m.baseTypeMatch = true
|
||||
else:
|
||||
else:
|
||||
result = userConvMatch(c, m, base(f), a, arg)
|
||||
|
||||
proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
@@ -663,7 +675,7 @@ proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
# iterators are not first class yet, so ignore them
|
||||
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}:
|
||||
copyCandidate(z, m)
|
||||
var r = typeRel(z.bindings, f, arg.sons[i].typ)
|
||||
var r = typeRel(z, f, arg.sons[i].typ)
|
||||
if r != isNone:
|
||||
case x.state
|
||||
of csEmpty, csNoMatch:
|
||||
@@ -687,7 +699,8 @@ proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||
else:
|
||||
# only one valid interpretation found:
|
||||
markUsed(arg, arg.sons[best].sym)
|
||||
result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best], argOrig)
|
||||
result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best],
|
||||
argOrig)
|
||||
|
||||
proc IndexTypesMatch*(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||
var m: TCandidate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
disabled: true
|
||||
output: "b"
|
||||
"""
|
||||
|
||||
type
|
||||
@@ -12,8 +12,8 @@ type
|
||||
TC = object of TB
|
||||
whatever: string
|
||||
|
||||
proc p(a: var TA) = nil
|
||||
proc p(b: var TB) = nil
|
||||
proc p(a: var TA) = echo "a"
|
||||
proc p(b: var TB) = echo "b"
|
||||
|
||||
var c: TC
|
||||
|
||||
|
||||
Reference in New Issue
Block a user