Skip to content

Commit c1f91c2

Browse files
authored
Overload resultion with generic variables an inheritance (#23870)
The test case diff is self explanatory
1 parent ccf90f5 commit c1f91c2

File tree

3 files changed

+117
-46
lines changed

3 files changed

+117
-46
lines changed

compiler/sigmatch.nim

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ type
8080
# or when the explain pragma is used. may be
8181
# triggered with an idetools command in the
8282
# future.
83-
inheritancePenalty: int # to prefer closest father object type
83+
# to prefer closest father object type
84+
inheritancePenalty: int
8485
firstMismatch*: MismatchInfo # mismatch info for better error messages
8586
diagnosticsEnabled*: bool
8687

@@ -95,6 +96,7 @@ type
9596

9697
const
9798
isNilConversion = isConvertible # maybe 'isIntConv' fits better?
99+
maxInheritancePenalty = high(int) div 2
98100

99101
proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
100102
proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
@@ -107,7 +109,7 @@ proc initCandidateAux(ctx: PContext,
107109
convMatches: 0, intConvMatches: 0, genericMatches: 0,
108110
state: csEmpty, firstMismatch: MismatchInfo(),
109111
callee: callee, call: nil, baseTypeMatch: false,
110-
genericConverter: false, inheritancePenalty: 0
112+
genericConverter: false, inheritancePenalty: -1
111113
)
112114

113115
proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
@@ -287,6 +289,15 @@ proc writeMatches*(c: TCandidate) =
287289
echo " conv matches: ", c.convMatches
288290
echo " inheritance: ", c.inheritancePenalty
289291

292+
proc cmpInheritancePenalty(a, b: int): int =
293+
var eb = b
294+
var ea = a
295+
if b < 0:
296+
eb = maxInheritancePenalty # defacto max penalty
297+
if a < 0:
298+
ea = maxInheritancePenalty
299+
eb - ea
300+
290301
proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
291302
result = a.exactMatches - b.exactMatches
292303
if result != 0: return
@@ -298,8 +309,7 @@ proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
298309
if result != 0: return
299310
result = a.convMatches - b.convMatches
300311
if result != 0: return
301-
# the other way round because of other semantics:
302-
result = b.inheritancePenalty - a.inheritancePenalty
312+
result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty)
303313
if result != 0: return
304314
if isFormal:
305315
# check for generic subclass relation
@@ -588,10 +598,9 @@ proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelat
588598
let firstField = if f.kind == tyTuple: 0
589599
else: 1
590600
for _, ff, aa in tupleTypePairs(f, a):
591-
let oldInheritancePenalty = c.inheritancePenalty
592601
var m = typeRel(c, ff, aa, flags)
593602
if m < isSubtype: return isNone
594-
if m == isSubtype and c.inheritancePenalty > oldInheritancePenalty:
603+
if m == isSubtype and aa.kind != tyNil and c.inheritancePenalty > -1:
595604
# we can't process individual element type conversions from a
596605
# type conversion for the whole tuple
597606
# subtype relations need type conversions when inheritance is used
@@ -1388,12 +1397,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
13881397
reduceToBase(a)
13891398
if effectiveArgType.kind == tyObject:
13901399
if sameObjectTypes(f, effectiveArgType):
1400+
c.inheritancePenalty = 0
13911401
result = isEqual
13921402
# elif tfHasMeta in f.flags: result = recordRel(c, f, a)
13931403
elif trIsOutParam notin flags:
1394-
var depth = isObjectSubtype(c, effectiveArgType, f, nil)
1395-
if depth > 0:
1396-
inc(c.inheritancePenalty, depth)
1404+
c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
1405+
if c.inheritancePenalty > 0:
13971406
result = isSubtype
13981407
of tyDistinct:
13991408
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
@@ -1562,7 +1571,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
15621571
if aAsObject.kind == tyObject and trIsOutParam notin flags:
15631572
let baseType = aAsObject.base
15641573
if baseType != nil:
1565-
c.inheritancePenalty += 1
1574+
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
15661575
let ret = typeRel(c, f, baseType, flags)
15671576
return if ret in {isEqual,isGeneric}: isSubtype else: ret
15681577

@@ -1659,7 +1668,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
16591668
depth = -1
16601669

16611670
if depth >= 0:
1662-
c.inheritancePenalty += depth
1671+
inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
16631672
# bug #4863: We still need to bind generic alias crap, so
16641673
# we cannot return immediately:
16651674
result = if depth == 0: isGeneric else: isSubtype
@@ -1677,19 +1686,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
16771686
considerPreviousT:
16781687
result = isNone
16791688
let oldInheritancePenalty = c.inheritancePenalty
1680-
var maxInheritance = 0
1689+
var minInheritance = maxInheritancePenalty
16811690
for branch in f.kids:
1682-
c.inheritancePenalty = 0
1691+
c.inheritancePenalty = -1
16831692
let x = typeRel(c, branch, aOrig, flags)
1684-
maxInheritance = max(maxInheritance, c.inheritancePenalty)
1685-
# 'or' implies maximum matching result:
1686-
if x > result: result = x
1693+
if x >= result:
1694+
if c.inheritancePenalty > -1:
1695+
minInheritance = min(minInheritance, c.inheritancePenalty)
1696+
result = x
16871697
if result >= isIntConv:
1698+
if minInheritance < maxInheritancePenalty:
1699+
c.inheritancePenalty = oldInheritancePenalty + minInheritance
16881700
if result > isGeneric: result = isGeneric
16891701
bindingRet result
16901702
else:
16911703
result = isNone
1692-
c.inheritancePenalty = oldInheritancePenalty + maxInheritance
16931704
of tyNot:
16941705
considerPreviousT:
16951706
if typeRel(c, f.elementType, aOrig, flags) != isNone:
@@ -1799,18 +1810,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
17991810
else:
18001811
# check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
18011812
if f.len > 0 and f[0].kind != tyNone:
1802-
let oldInheritancePenalty = c.inheritancePenalty
18031813
result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam})
18041814
if doBindGP and result notin {isNone, isGeneric}:
18051815
let concrete = concreteType(c, a, f)
18061816
if concrete == nil: return isNone
18071817
put(c, f, concrete)
1808-
# bug #6526
18091818
if result in {isEqual, isSubtype}:
1810-
# 'T: Class' is a *better* match than just 'T'
1811-
# but 'T: Subclass' is even better:
1812-
c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
1813-
100 * ord(result == isEqual)
18141819
result = isGeneric
18151820
elif a.kind == tyTypeDesc:
18161821
# somewhat special typing rule, the following is illegal:
@@ -1843,7 +1848,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
18431848
elif x.kind == tyGenericParam:
18441849
result = isGeneric
18451850
else:
1851+
# This is the bound type - can't benifit from these tallies
1852+
let
1853+
inheritancePenaltyOld = c.inheritancePenalty
18461854
result = typeRel(c, x, a, flags) # check if it fits
1855+
c.inheritancePenalty = inheritancePenaltyOld
18471856
if result > isGeneric: result = isGeneric
18481857
of tyStatic:
18491858
let prev = idTableGet(c.bindings, f)
@@ -2256,8 +2265,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
22562265
inc(m.genericMatches)
22572266
if arg.typ == nil:
22582267
result = arg
2259-
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
2260-
m.inheritancePenalty > oldInheritancePenalty:
2268+
elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
22612269
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
22622270
elif arg.typ.isEmptyContainer:
22632271
result = arg.copyTree

tests/overload/toverload_issues.nim

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,30 @@ testPred(1)
7777

7878

7979

80-
# bug #6526
81-
type
82-
BaseObj = ref object of RootObj
83-
DerivedObj = ref object of BaseObj
84-
OtherDerivate = ref object of BaseObj
85-
86-
proc `==`*[T1, T2: BaseObj](a: T1, b: T2): bool =
87-
echo "baseobj =="
88-
return true
89-
90-
let a = DerivedObj()
91-
let b = DerivedObj()
92-
echo a == b
93-
94-
proc `==`*[T1, T2: OtherDerivate](a: T1, b: T2): bool =
95-
echo "even better! =="
96-
return true
97-
98-
let a2 = OtherDerivate()
99-
let b2 = OtherDerivate()
100-
echo a2 == b2
80+
block: # bug #6526
81+
type
82+
BaseObj = ref object of RootObj
83+
DerivedObj = ref object of BaseObj
84+
OtherDerivate = ref object of BaseObj
85+
86+
proc p[T](a: T, b: T): bool =
87+
assert false
88+
89+
proc p[T1, T2: BaseObj](a: T1, b: T2): bool =
90+
echo "baseobj =="
91+
return true
92+
93+
let a = DerivedObj()
94+
let b = DerivedObj()
95+
echo p(a,b)
96+
97+
proc p[T1, T2: OtherDerivate](a: T1, b: T2): bool =
98+
echo "even better! =="
99+
return true
100+
101+
let a2 = OtherDerivate()
102+
let b2 = OtherDerivate()
103+
echo p(a2, b2)
101104

102105

103106

tests/overload/toverload_various.nim

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,63 @@ block:
506506
doAssert(p2(F(float,1.0),F(float,2)) == 3.0)
507507
doAssert(p2(F(float,1.0),F(float,2.0)) == 3.0)
508508
#doAssert(p2(F(float,1),F(int,2.0)) == 3.0)
509+
510+
block: # PR #23870
511+
type
512+
A {.inheritable.} = object
513+
B = object of A
514+
C = object of B
515+
516+
proc p[T: A](x: T): int = 0
517+
proc p[T: B](x: T): int = 1
518+
519+
proc d(x: A): int = 0
520+
proc d(x: B): int = 1
521+
522+
proc g[T:A](x: typedesc[T]): int = 0
523+
proc g[T: B](x: typedesc[T]): int = 1
524+
525+
proc f[T](x: typedesc[T]): int = 0
526+
proc f[T:B](x: typedesc[T]): int = 1
527+
528+
assert p(C()) == 1
529+
assert d(C()) == 1
530+
assert g(C) == 1
531+
assert f(C) == 1
532+
533+
block: # PR #23870
534+
type
535+
A = object of RootObj
536+
PT = proc(ev: A) {.closure.}
537+
sdt = seq[(PT, PT)]
538+
539+
proc encap() =
540+
proc p(a: A) {.closure.} =
541+
discard
542+
543+
var s: sdt
544+
s.add (p, nil)
545+
546+
encap()
547+
548+
block: # PR #23870
549+
type
550+
A = object of RootObj
551+
B = object of A
552+
C = object of B
553+
554+
proc p(a: B | RootObj): int =
555+
0
556+
557+
proc p(a: A | A): int =
558+
1
559+
560+
assert p(C()) == 0
561+
562+
proc d(a: RootObj | B): int =
563+
0
564+
565+
proc d(a: A | A): int =
566+
1
567+
568+
assert d(C()) == 0

0 commit comments

Comments
 (0)