Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix subtype match of generic object types #24430

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 21 additions & 1 deletion compiler/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 changes: 18 additions & 0 deletions tests/objects/tgenericsubtype.nim
Original file line number Diff line number Diff line change
@@ -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)