diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a1a1d21d7f..848cc684d6 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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 diff --git a/compiler/types.nim b/compiler/types.nim index bc6de5098b..bf32daa056 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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 diff --git a/tests/objects/tgenericsubtype.nim b/tests/objects/tgenericsubtype.nim new file mode 100644 index 0000000000..c2618654c6 --- /dev/null +++ b/tests/objects/tgenericsubtype.nim @@ -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)