From 511ab72342be5b194919c9af0aa7754e42aba156 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 12 Nov 2024 16:31:59 +0300 Subject: [PATCH] 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. --- compiler/sigmatch.nim | 12 +++++++++++- compiler/types.nim | 22 +++++++++++++++++++++- tests/objects/tgenericsubtype.nim | 18 ++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/objects/tgenericsubtype.nim 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)