From 39a88ffa2a4aadd472e6bb525f4be5ee2a036a13 Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 9 Nov 2024 19:05:15 +0300 Subject: [PATCH 01/10] test always instantiating nominal generic insts --- compiler/semtypinst.nim | 15 +++++++++------ tests/arc/tphantomgeneric1.nim | 19 +++++++++++++++++++ tests/arc/tphantomgeneric2.nim | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 tests/arc/tphantomgeneric1.nim create mode 100644 tests/arc/tphantomgeneric2.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 2aca2c7a4baf..0848adf9b843 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -80,7 +80,7 @@ type owner*: PSym # where this instantiation comes from recursionLimit: int -proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType +proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode @@ -95,8 +95,8 @@ template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code debug t writeStackTrace() -proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = - result = replaceTypeVarsTAux(cl, t) +proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType, isInstValue = false): PType = + result = replaceTypeVarsTAux(cl, t, isInstValue) checkMetaInvariants(cl, result) proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode = @@ -481,7 +481,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = return let bbody = last body - var newbody = replaceTypeVarsT(cl, bbody) + var newbody = replaceTypeVarsT(cl, bbody, isInstValue = true) cl.skipTypedesc = oldSkipTypedesc newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) result.flags = result.flags + newbody.flags - tfInstClearedFlags @@ -578,7 +578,7 @@ proc propagateFieldFlags(t: PType, n: PNode) = propagateFieldFlags(t, son) else: discard -proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = +proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): PType = template bailout = if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags): # In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see @@ -712,7 +712,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = propagateToOwner(result, result.last) else: - if containsGenericType(t): + if containsGenericType(t) or + # nominal types as direct generic instantiation values + # are re-instantiated even if they don't contain generic fields + (isInstValue and t.kind in {tyDistinct, tyEnum, tyObject}): #if not cl.allowMetaTypes: bailout() result = instCopyType(cl, t) diff --git a/tests/arc/tphantomgeneric1.nim b/tests/arc/tphantomgeneric1.nim new file mode 100644 index 000000000000..8ca36053d3be --- /dev/null +++ b/tests/arc/tphantomgeneric1.nim @@ -0,0 +1,19 @@ +discard """ + output: ''' +int +float +''' +""" + +# issue #22479 + +type Obj[T] = object + +proc `=destroy`[T](self: var Obj[T]) = + echo T + +block: + let intObj = Obj[int]() + +block: + let floatObj = Obj[float]() diff --git a/tests/arc/tphantomgeneric2.nim b/tests/arc/tphantomgeneric2.nim new file mode 100644 index 000000000000..f4023db43f1e --- /dev/null +++ b/tests/arc/tphantomgeneric2.nim @@ -0,0 +1,33 @@ +discard """ + output: ''' +created Phantom[system.float] with value 1 +created Phantom[system.string] with value 2 +created Phantom[system.byte] with value 3 +destroyed Phantom[system.byte] with value 3 +destroyed Phantom[system.string] with value 2 +destroyed Phantom[system.float] with value 1 +''' +""" + +# issue #24374 + +type Phantom[T] = object + value: int + # markerField: T + +proc initPhantom[T](value: int): Phantom[T] = + doAssert value >= 0 + echo "created " & $Phantom[T] & " with value " & $value + result = Phantom[T](value: value) + +proc `=wasMoved`[T](x: var Phantom[T]) = + x.value = -1 + +proc `=destroy`[T](x: Phantom[T]) = + if x.value >= 0: + echo "destroyed " & $Phantom[T] & " with value " & $x.value + +let + x = initPhantom[float](1) + y = initPhantom[string](2) + z = initPhantom[byte](3) From 46af4dd2e3ce96f259bf192f7500f582d06ab5bc Mon Sep 17 00:00:00 2001 From: metagn Date: Sat, 9 Nov 2024 21:16:07 +0300 Subject: [PATCH 02/10] fix enums, add preexisting bug tests --- compiler/semtypinst.nim | 2 +- tests/destructor/tinvalid_rebind.nim | 1 + tests/destructor/tinvalid_rebind_nonempty.nim | 17 +++++++++++++ tests/objects/tgenericsubtype.nim | 24 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/destructor/tinvalid_rebind_nonempty.nim create mode 100644 tests/objects/tgenericsubtype.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0848adf9b843..7a8be48c40a3 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -715,7 +715,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): if containsGenericType(t) or # nominal types as direct generic instantiation values # are re-instantiated even if they don't contain generic fields - (isInstValue and t.kind in {tyDistinct, tyEnum, tyObject}): + (isInstValue and t.kind in {tyDistinct, tyObject}): #if not cl.allowMetaTypes: bailout() result = instCopyType(cl, t) diff --git a/tests/destructor/tinvalid_rebind.nim b/tests/destructor/tinvalid_rebind.nim index 0f15c8f9e778..53b7d5a44c63 100644 --- a/tests/destructor/tinvalid_rebind.nim +++ b/tests/destructor/tinvalid_rebind.nim @@ -1,4 +1,5 @@ discard """ +disabled: true joinable: false cmd: "nim check $file" errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)" diff --git a/tests/destructor/tinvalid_rebind_nonempty.nim b/tests/destructor/tinvalid_rebind_nonempty.nim new file mode 100644 index 000000000000..cf9b930560c3 --- /dev/null +++ b/tests/destructor/tinvalid_rebind_nonempty.nim @@ -0,0 +1,17 @@ +discard """ +disabled: true +joinable: false +cmd: "nim check $file" +errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)" +line: 14 +""" + +type + Foo[T] = object + x: T + +proc main = + var f: Foo[int] + +proc `=destroy`[T](f: var Foo[T]) = + discard diff --git a/tests/objects/tgenericsubtype.nim b/tests/objects/tgenericsubtype.nim new file mode 100644 index 000000000000..12ff0beea8b2 --- /dev/null +++ b/tests/objects/tgenericsubtype.nim @@ -0,0 +1,24 @@ +discard """ + disabled: true +""" + +# currently broken + +block: + type + Foo[T] = object of RootObj + x: T + Bar = object of Foo[int] + + proc foo(x: typedesc[Foo]) = discard + + foo(Bar) + +block: + type + Foo[T] = object of RootObj + Bar = object of Foo[int] + + proc foo(x: typedesc[Foo]) = discard + + foo(Bar) From f2cd8bb1a2f78d23a36d38cee7bc9f1050627ad7 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 11:33:55 +0300 Subject: [PATCH 03/10] try generic subtype fix first --- compiler/sigmatch.nim | 12 +++++++++++- tests/objects/tgenericsubtype.nim | 6 ------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a1a1d21d7f68..0fb657e2fc7f 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 = + if not (f.sym != nil and f.sym.typ.kind == tyGenericBody): + return false + if a.typeInst != nil: + let aBaseSym = a.typeInst.genericHead.sym + result = aBaseSym != nil and (f.sym == aBaseSym) + else: + # assume a is generic + result = a.sym != nil and a.sym.typ.kind == tyGenericBody and f.sym == a.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/tests/objects/tgenericsubtype.nim b/tests/objects/tgenericsubtype.nim index 12ff0beea8b2..f6ad6c81a141 100644 --- a/tests/objects/tgenericsubtype.nim +++ b/tests/objects/tgenericsubtype.nim @@ -1,9 +1,3 @@ -discard """ - disabled: true -""" - -# currently broken - block: type Foo[T] = object of RootObj From 21f6b4d0c9d594082571274439586893bd53616b Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 14:27:10 +0300 Subject: [PATCH 04/10] test slight refactor --- compiler/sigmatch.nim | 12 ++++++------ compiler/types.nim | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0fb657e2fc7f..848cc684d602 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -643,14 +643,14 @@ proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = 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 - if a.typeInst != nil: - let aBaseSym = a.typeInst.genericHead.sym - result = aBaseSym != nil and (f.sym == aBaseSym) - else: - # assume a is generic - result = a.sym != nil and a.sym.typ.kind == tyGenericBody and f.sym == a.sym + 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 diff --git a/compiler/types.nim b/compiler/types.nim index bc6de5098bf0..d4f74a18d9f7 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -2000,3 +2000,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 From 14f03fc7095ef85bc83cdca8dc58a17c50e622f4 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 15:24:24 +0300 Subject: [PATCH 05/10] try destructor fix --- compiler/liftdestructors.nim | 4 +++ compiler/semstmts.nim | 31 +++++++++++++------ tests/destructor/tinvalid_rebind.nim | 3 +- tests/destructor/tinvalid_rebind_nonempty.nim | 5 ++- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 69d457a566e5..09fc4e2ff85a 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -1276,6 +1276,10 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf ## The later 'injectdestructors' pass depends on it. if orig == nil or {tfCheckedForDestructor, tfHasMeta} * orig.flags != {}: return incl orig.flags, tfCheckedForDestructor + # for user defined generic destructors: + let origRoot = genericRoot(orig) + if origRoot != nil: + incl origRoot.flags, tfCheckedForDestructor let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink}) if isEmptyContainer(skipped) or skipped.kind == tyStatic: return diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 60778d25d687..146ae8fe9166 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2035,14 +2035,27 @@ proc canonType(c: PContext, t: PType): PType = else: result = t -proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) = - var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj) - if sfOverridden notin prevOp.flags: +proc prevDestructor(c: PContext; op: TTypeAttachedOp; prevOp: PSym; obj: PType; info: TLineInfo) = + var msg = "cannot bind another '" & AttachedOpToStr[op] & "' to: " & typeToString(obj) + if prevOp == nil: + # happens if the destructor was implicitly constructed for a specific instance, + # not the entire generic type + msg.add "; previous declaration was constructed implicitly" + elif sfOverridden notin prevOp.flags: msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info) else: msg.add "; previous declaration was here: " & (c.config $ prevOp.info) localError(c.config, info, errGenerated, msg) +proc checkedForDestructor(t: PType): bool = + if tfCheckedForDestructor in t.flags: + return true + # maybe another instance was instantiated, marking the generic root: + let root = genericRoot(t) + if root != nil and tfCheckedForDestructor in root.flags: + return true + result = false + proc whereToBindTypeHook(c: PContext; t: PType): PType = result = t while true: @@ -2076,10 +2089,10 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let ao = getAttachedOp(c.graph, obj, op) if ao == s: discard "forward declared destructor" - elif ao.isNil and tfCheckedForDestructor notin obj.flags: + elif ao.isNil and not checkedForDestructor(obj): setAttachedOp(c.graph, c.module.position, obj, op, s) else: - prevDestructor(c, ao, obj, n.info) + prevDestructor(c, op, ao, obj, n.info) noError = true if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, @@ -2123,10 +2136,10 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let ao = getAttachedOp(c.graph, obj, op) if ao == s: discard "forward declared destructor" - elif ao.isNil and tfCheckedForDestructor notin obj.flags: + elif ao.isNil and not checkedForDestructor(obj): setAttachedOp(c.graph, c.module.position, obj, op, s) else: - prevDestructor(c, ao, obj, n.info) + prevDestructor(c, op, ao, obj, n.info) noError = true if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, @@ -2217,10 +2230,10 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = let ao = getAttachedOp(c.graph, obj, k) if ao == s: discard "forward declared op" - elif ao.isNil and tfCheckedForDestructor notin obj.flags: + elif ao.isNil and not checkedForDestructor(obj): setAttachedOp(c.graph, c.module.position, obj, k, s) else: - prevDestructor(c, ao, obj, n.info) + prevDestructor(c, k, ao, obj, n.info) if obj.owner.getModule != s.getModule: localError(c.config, n.info, errGenerated, "type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") diff --git a/tests/destructor/tinvalid_rebind.nim b/tests/destructor/tinvalid_rebind.nim index 53b7d5a44c63..1dca8e993dd9 100644 --- a/tests/destructor/tinvalid_rebind.nim +++ b/tests/destructor/tinvalid_rebind.nim @@ -1,8 +1,7 @@ discard """ -disabled: true joinable: false cmd: "nim check $file" -errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)" +errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed implicitly" line: 14 """ diff --git a/tests/destructor/tinvalid_rebind_nonempty.nim b/tests/destructor/tinvalid_rebind_nonempty.nim index cf9b930560c3..ffa886700b10 100644 --- a/tests/destructor/tinvalid_rebind_nonempty.nim +++ b/tests/destructor/tinvalid_rebind_nonempty.nim @@ -1,9 +1,8 @@ discard """ -disabled: true joinable: false cmd: "nim check $file" -errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed here implicitly: tinvalid_rebind.nim(12, 7)" -line: 14 +errormsg: "cannot bind another '=destroy' to: Foo; previous declaration was constructed implicitly" +line: 15 """ type From ebc39d71416093453602543d06ff304aa41e90d6 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 15:35:48 +0300 Subject: [PATCH 06/10] use separate type flag --- compiler/ast.nim | 2 ++ compiler/liftdestructors.nim | 2 +- compiler/semstmts.nim | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 2f6c272320ea..cc3f4dcd998e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -447,6 +447,8 @@ const tfReturnsNew* = tfInheritable tfNonConstExpr* = tfExplicitCallConv ## tyFromExpr where the expression shouldn't be evaluated as a static value + tfGenericHasDestructor* = tfExplicitCallConv + ## tyGenericBody where an instance has a generated destructor skError* = skUnknown var diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 09fc4e2ff85a..6aa263440179 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -1279,7 +1279,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf # for user defined generic destructors: let origRoot = genericRoot(orig) if origRoot != nil: - incl origRoot.flags, tfCheckedForDestructor + incl origRoot.flags, tfGenericHasDestructor let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink}) if isEmptyContainer(skipped) or skipped.kind == tyStatic: return diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 146ae8fe9166..6718fa24e7f5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2052,7 +2052,7 @@ proc checkedForDestructor(t: PType): bool = return true # maybe another instance was instantiated, marking the generic root: let root = genericRoot(t) - if root != nil and tfCheckedForDestructor in root.flags: + if root != nil and tfGenericHasDestructor in root.flags: return true result = false From cd494388d469218421b2eabd70ebc2d54f501d37 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 16:22:29 +0300 Subject: [PATCH 07/10] test ref objects --- compiler/semtypinst.nim | 5 +++-- compiler/types.nim | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 7a8be48c40a3..217488cb8d09 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -715,13 +715,14 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): if containsGenericType(t) or # nominal types as direct generic instantiation values # are re-instantiated even if they don't contain generic fields - (isInstValue and t.kind in {tyDistinct, tyObject}): + (isInstValue and (t.kind in {tyDistinct, tyObject} or isRefPtrObject(t))): #if not cl.allowMetaTypes: bailout() result = instCopyType(cl, t) result.size = -1 # needs to be recomputed #if not cl.allowMetaTypes: cl.localCache[t.itemId] = result + let propagateInstValue = isInstValue and isRefPtrObject(t) for i, resulti in result.ikids: if resulti != nil: @@ -731,7 +732,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false): typeToString(result[i], preferDesc) & "' inside of type definition: '" & t.owner.name.s & "'; Maybe generic arguments are missing?") - var r = replaceTypeVarsT(cl, resulti) + var r = replaceTypeVarsT(cl, resulti, isInstValue = propagateInstValue) if result.kind == tyObject: # carefully coded to not skip the precious tyGenericInst: let r2 = r.skipTypes({tyAlias, tySink, tyOwned}) diff --git a/compiler/types.nim b/compiler/types.nim index d4f74a18d9f7..bf32daa05640 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: From 813d650cf1b9dc58f694d330b5141de5e7aa8f24 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 17:13:35 +0300 Subject: [PATCH 08/10] test preexisting jsonutils bug --- lib/std/jsonutils.nim | 2 +- tests/stdlib/tjsonutils.nim | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 2d28748ce88b..a86dd5f90048 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -112,7 +112,7 @@ macro initCaseObject(T: typedesc, fun: untyped): untyped = else: raiseAssert $t.kind # xxx `nnkPtrTy` could be handled too doAssert t2.kind == nnkRecList result = newTree(nnkObjConstr) - result.add sym + result.add T for ti in t2: if ti.kind == nnkRecCase: let key = ti[0][0] diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 9acf4c9e545e..9d9188d755a4 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -206,6 +206,19 @@ template fn() = # sanity check: nesting inside a tuple testRoundtrip((Foo[int](x0: 1.5, t1: false, z2: 6), "foo")): """[{"x0":1.5,"t1":false,"z2":6,"x1":""},"foo"]""" + block: # generic case object using generic type + type Foo[T] = ref object + x0: float + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + x2: T + testRoundtrip(Foo[float](t1: true, z1: 5, x1: "bar", x2: 2.5)): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar","x2":2.5}""" + testRoundtrip(Foo[int](x0: 1.5, t1: false, z2: 6, x2: 2)): """{"x0":1.5,"t1":false,"z2":6,"x1":"","x2":2}""" + # sanity check: nesting inside a tuple + testRoundtrip((Foo[int](x0: 1.5, t1: false, z2: 6, x2: 2), "foo")): """[{"x0":1.5,"t1":false,"z2":6,"x1":"","x2":2},"foo"]""" + block: # case object: 2 discriminants, `when` branch, range discriminant type Foo[T] = object case t1: bool From 95d70147e84350e24779f73a2e71f6c0f92d3da2 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 10 Nov 2024 18:33:38 +0300 Subject: [PATCH 09/10] fix proc instantiations for dereferenced ref object types --- compiler/types.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/types.nim b/compiler/types.nim index bf32daa05640..587adb25c209 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1124,6 +1124,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = result = false template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = + # XXX should be removed in favor of just symbol/ID equalities, + # structural equality is wrong in general if tfFromGeneric notin a.flags + b.flags: # fast case: id comparison suffices: result = a.id == b.id @@ -1309,6 +1311,12 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = withoutShallowFlags: ifFastObjectTypeCheckFailed(a, b): cycleCheck() + if a.typeInst != nil and b.typeInst != nil: + # this is required because of `ref object`s, + # the value of their dereferences are not wrapped in `tyGenericInst`, + # so we need to check the generic parameters here + for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1): + if not sameTypeAux(ff, aa, c): return false result = sameObjectStructures(a, b, c) and sameFlags(a, b) of tyDistinct: cycleCheck() From 08e9625bd590ae02ecf75355bbc0965ae96835ea Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 12 Nov 2024 17:19:06 +0300 Subject: [PATCH 10/10] while CI is running, fix comments --- compiler/types.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 587adb25c209..66df653a2fac 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1124,8 +1124,6 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = result = false template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = - # XXX should be removed in favor of just symbol/ID equalities, - # structural equality is wrong in general if tfFromGeneric notin a.flags + b.flags: # fast case: id comparison suffices: result = a.id == b.id @@ -1317,12 +1315,16 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = # so we need to check the generic parameters here for ff, aa in underspecifiedPairs(a.typeInst, b.typeInst, 1, -1): if not sameTypeAux(ff, aa, c): return false + # XXX should be removed in favor of above lines, + # structural equality is wrong in general: result = sameObjectStructures(a, b, c) and sameFlags(a, b) of tyDistinct: cycleCheck() if c.cmp == dcEq: if sameFlags(a, b): ifFastObjectTypeCheckFailed(a, b): + # XXX should be removed in favor of checking generic params, + # structural equality is wrong in general: result = sameTypeAux(a.elementType, b.elementType, c) else: result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)