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:
metagn
2024-11-12 16:31:59 +03:00
committed by GitHub
parent 45e21ce8f1
commit 511ab72342
3 changed files with 50 additions and 2 deletions

View File

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

View File

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

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