Skip to content

Commit

Permalink
fix subtype match of generic object types
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Nov 12, 2024
1 parent 76c5f16 commit 7f90bfa
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
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)

0 comments on commit 7f90bfa

Please sign in to comment.