mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-12 06:18:51 +00:00
fix subtype match of generic object types (#24430)
split from #24425 Matching `tyGenericBody` performs a match on the last child of the generic body, in this case the uninstantied `tyObject` type. If the object contains no generic fields, this ends up being the same type as all instantiated ones, but if it does, a new type is created. This fails the `sameObjectTypes` check that subtype matching for object types uses. To fix this, also consider that the pattern type could be the generic uninstantiated object type of the matched type in subtype matching.
This commit is contained in:
@@ -642,12 +642,22 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
|
||||
if x == nil:
|
||||
put(c, fGenericOrigin[i], last[i])
|
||||
|
||||
proc isGenericObjectOf(f, a: PType): bool =
|
||||
## checks if `f` is an unparametrized generic type
|
||||
## that `a` is an instance of
|
||||
if not (f.sym != nil and f.sym.typ.kind == tyGenericBody):
|
||||
# covers the case where `f` is the last child (body) of the `tyGenericBody`
|
||||
return false
|
||||
let aRoot = genericRoot(a)
|
||||
# use sym equality to check if the `tyGenericBody` types are equal
|
||||
result = aRoot != nil and f.sym == aRoot.sym
|
||||
|
||||
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||
var t = a
|
||||
assert t.kind == tyObject
|
||||
var depth = 0
|
||||
var last = a
|
||||
while t != nil and not sameObjectTypes(f, t):
|
||||
while t != nil and not (sameObjectTypes(f, t) or isGenericObjectOf(f, t)):
|
||||
if t.kind != tyObject: # avoid entering generic params etc
|
||||
return -1
|
||||
t = t.baseClass
|
||||
|
||||
@@ -1938,6 +1938,9 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc isRefPtrObject*(t: PType): bool =
|
||||
t.kind in {tyRef, tyPtr} and tfRefsAnonObj in t.flags
|
||||
|
||||
proc nominalRoot*(t: PType): PType =
|
||||
## the "name" type of a given instance of a nominal type,
|
||||
## i.e. the type directly associated with the symbol where the root
|
||||
@@ -1970,7 +1973,7 @@ proc nominalRoot*(t: PType): PType =
|
||||
result = result.skipModifier[0]
|
||||
let val = result.skipModifier
|
||||
if val.kind in {tyDistinct, tyEnum, tyObject} or
|
||||
(val.kind in {tyRef, tyPtr} and tfRefsAnonObj in val.flags):
|
||||
isRefPtrObject(val):
|
||||
# atomic nominal types, this generic body is attached to them
|
||||
discard
|
||||
else:
|
||||
@@ -2000,3 +2003,20 @@ proc nominalRoot*(t: PType): PType =
|
||||
# skips all typeclasses
|
||||
# is this correct for `concept`?
|
||||
result = nil
|
||||
|
||||
proc genericRoot*(t: PType): PType =
|
||||
## gets the root generic type (`tyGenericBody`) from `t`,
|
||||
## if `t` is a generic type or the body of a generic instantiation
|
||||
case t.kind
|
||||
of tyGenericBody:
|
||||
result = t
|
||||
of tyGenericInst, tyGenericInvocation:
|
||||
result = t.genericHead
|
||||
else:
|
||||
if t.typeInst != nil:
|
||||
result = t.typeInst.genericHead
|
||||
elif t.sym != nil and t.sym.typ.kind == tyGenericBody:
|
||||
# can happen if `t` is the last child (body) of the generic body
|
||||
result = t.sym.typ
|
||||
else:
|
||||
result = nil
|
||||
|
||||
18
tests/objects/tgenericsubtype.nim
Normal file
18
tests/objects/tgenericsubtype.nim
Normal file
@@ -0,0 +1,18 @@
|
||||
block: # has generic field
|
||||
type
|
||||
Foo[T] = object of RootObj
|
||||
x: T
|
||||
Bar = object of Foo[int]
|
||||
|
||||
proc foo(x: typedesc[Foo]) = discard
|
||||
|
||||
foo(Bar)
|
||||
|
||||
block: # no generic field
|
||||
type
|
||||
Foo[T] = object of RootObj
|
||||
Bar = object of Foo[int]
|
||||
|
||||
proc foo(x: typedesc[Foo]) = discard
|
||||
|
||||
foo(Bar)
|
||||
Reference in New Issue
Block a user