From a04f720217f3d7c34506bc85e91c9e47ef3873fa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:41:54 +0800 Subject: [PATCH 1/5] fixes #25482; ICE leaking temporary 3 slotTempInt (#25483) fixes #25482 --- compiler/vmgen.nim | 2 ++ tests/vm/tvmmisc.nim | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9576fd3a99..ad009298ca 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -803,6 +803,8 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let first = c.genx(newIntTypeNode(firstOrd(c.config, t), intType)) let last = c.genx(newIntTypeNode(lastOrd(c.config, t), intType)) c.gABC(n, opcNarrowR, dest, first, last) + c.freeTemp(first) + c.freeTemp(last) proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index e2d979fad6..35ac6f0ccf 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -821,3 +821,10 @@ proc g1314(_: static bool) = discard proc g1314(_: int) = discard proc y1314() = g1314((; let k = 0; k)) y1314() + +proc myProc(first: range[0..100]) = + var x = first + while x > 0: + dec(x) + +const r = (myProc(3); 1) From 296b2789b52079d55f9f472336d122b197fa9f20 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 6 Feb 2026 00:54:04 +0100 Subject: [PATCH 2/5] Fixes #25340 (#25389) --- compiler/semtypes.nim | 2 +- compiler/sigmatch.nim | 50 ++++++++++++++++++++-------------------- tests/typerel/t25340.nim | 16 +++++++++++++ 3 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 tests/typerel/t25340.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 263ec13e74..f93db9e2a1 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1014,7 +1014,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} = proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int, obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool = if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or - (innerObj != nil and obj.sym.id == innerObj.sym.id): + (innerObj != nil and obj.id == innerObj.id): localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'") result = false elif obj.kind == tyObject: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6c8a01bd52..d97148baef 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1678,7 +1678,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif a.kind == tyGenericInst: if roota.base == rootf.base: let nextFlags = flags + {trNoCovariance} - var hasCovariance = false # YYYY result = isEqual @@ -1690,7 +1689,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if res notin {isEqual, isGeneric}: if trNoCovariance notin flags and ff.kind == aa.kind: let paramFlags = rootf.base[i-1].flags - hasCovariance = + let hasCovariance = if tfCovariant in paramFlags: if tfWeakCovariant in paramFlags: isCovariantPtr(c, ff, aa) @@ -1701,35 +1700,36 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, typeRel(c, aa, ff, flags) == isSubtype if hasCovariance: continue + result = isNone + break - return isNone - if prev == nil: put(c, f, a) - else: - let fKind = rootf.last.kind - if fKind in {tyAnd, tyOr}: - result = typeRel(c, last(f), a, flags) - if result != isNone: put(c, f, a) + if result != isNone: + if prev == nil: put(c, f, a) return - var aAsObject = roota.last + let fKind = rootf.last.kind + if fKind in {tyAnd, tyOr}: + result = typeRel(c, last(f), a, flags) + if result != isNone: put(c, f, a) + return - if fKind in {tyRef, tyPtr}: - if aAsObject.kind == tyObject: - # bug #7600, tyObject cannot be passed - # as argument to tyRef/tyPtr - return isNone - elif aAsObject.kind == fKind: - aAsObject = aAsObject.base + var aAsObject = roota.last - if aAsObject.kind == tyObject and trIsOutParam notin flags: - let baseType = aAsObject.base - if baseType != nil: - if tfFinal notin aAsObject.flags: - inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0) - let ret = typeRel(c, f, baseType, flags) - return if ret in {isEqual,isGeneric}: isSubtype else: ret + if fKind in {tyRef, tyPtr}: + if aAsObject.kind == tyObject: + # bug #7600, tyObject cannot be passed + # as argument to tyRef/tyPtr + return isNone + elif aAsObject.kind == fKind: + aAsObject = aAsObject.base - result = isNone + if aAsObject.kind == tyObject and trIsOutParam notin flags: + let baseType = aAsObject.base + if baseType != nil: + if tfFinal notin aAsObject.flags: + inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0) + let ret = typeRel(c, f, baseType, flags) + return if ret in {isEqual,isGeneric}: isSubtype else: ret else: assert last(origF) != nil result = typeRel(c, last(origF), a, flags) diff --git a/tests/typerel/t25340.nim b/tests/typerel/t25340.nim new file mode 100644 index 0000000000..2ddcd4cbce --- /dev/null +++ b/tests/typerel/t25340.nim @@ -0,0 +1,16 @@ + +type + Foo[T] = object of T + +template inheritanceCheck(a, b: untyped) = + doAssert a is b + doAssert b isnot a + +inheritanceCheck Foo[RootObj], RootObj + +inheritanceCheck Foo[Foo[RootObj]], RootObj +inheritanceCheck Foo[Foo[RootObj]], Foo[RootObj] + +inheritanceCheck Foo[Foo[Foo[RootObj]]], RootObj +inheritanceCheck Foo[Foo[Foo[RootObj]]], Foo[RootObj] +inheritanceCheck Foo[Foo[Foo[RootObj]]], Foo[Foo[RootObj]] From 12a2333817ad8864cbb2e36367701c908631c787 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 6 Feb 2026 09:19:46 +0800 Subject: [PATCH 3/5] fixes #25464; gives a deprecated warning when `=dup` is not provided while there being a custom `=copy` (#25485) Gives a deprecated warning to keep backwards compatibility fixes #25464 --- compiler/liftdestructors.nim | 13 ++++++++++ tests/destructor/tdup_from_copy.nim | 39 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/destructor/tdup_from_copy.nim diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index c3b6ba0886..24badb364a 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -558,6 +558,15 @@ proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode = v.addVar(result, value) body.add v +proc errorDupCustomCopy(c: var TLiftCtx; t: PType) {.inline.} = + ## Emit an error when generating `=dup` code and a custom `=copy` hook + ## exists + if c.kind == attachedDup: + let op2 = getAttachedOp(c.g, t, attachedAsgn) + if op2 != nil and sfOverridden in op2.flags: + localError(c.g.config, c.info, + "'=dup' is not provided while a custom '=copy' is defined for type '" & typeToString(t) & "'") + proc addIncStmt(c: var TLiftCtx; body, i: PNode) = let incCall = genBuiltin(c, mInc, "inc", i) incCall.add lowerings.newIntLit(c.g, c.info, 1) @@ -1056,6 +1065,9 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind == attachedDup: var op2 = getAttachedOp(c.g, t, attachedAsgn) if op2 != nil and sfOverridden in op2.flags: + # warn if a custom '=copy' exists but no '=dup' is provided + message(c.g.config, c.info, warnDeprecated, + "'=dup' is not provided while a custom '=copy' is defined for type '" & typeToString(t) & "'; this will become a compile time error in the future") #markUsed(c.g.config, c.info, op, c.g.usageSym) onUse(c.info, op2) body.add newHookCall(c, t.assignment, x, y) @@ -1065,6 +1077,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = fillBodyObjT(c, t, body, x, y) of tyDistinct: if not considerUserDefinedOp(c, t, body, x, y): + errorDupCustomCopy(c, t) fillBody(c, t.elementType, body, x, y) of tyTuple: fillBodyTup(c, t, body, x, y) diff --git a/tests/destructor/tdup_from_copy.nim b/tests/destructor/tdup_from_copy.nim new file mode 100644 index 0000000000..4a8029baff --- /dev/null +++ b/tests/destructor/tdup_from_copy.nim @@ -0,0 +1,39 @@ +discard """ + errormsg: "'=dup' is not provided while a custom '=copy' is defined for type 'Foo'" +""" + +type Foo = distinct int + +var counter = 0 + +proc `=destroy`(pkt: var Foo) = + if cast[int](pkt) != 0: + echo cast[int](pkt) + +proc `=copy`(a: var Foo, b: Foo) = + if cast[int](a) == cast[int](b): + return + + `=destroy`(a) + if cast[int](b) == 0: + zeroMem(addr a, sizeof(Foo)) + else: + counter += 1 + copyMem(addr a, addr counter, sizeof(Foo)) + echo "copy!" + +proc makeFoo(): Foo = + counter += 1 + cast[Foo](counter) + + +type Bar = object + val: Foo + + +proc consume(x: sink Bar) = + discard + +let x = Bar(val: makeFoo()) +consume(x) +discard x From 513c9aa69a59d4dd414363d518d317b6e614f2ee Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 8 Feb 2026 03:46:50 +0800 Subject: [PATCH 4/5] fixes #25488; Strings can be compared against nil (#25489) fixes #25488 ref https://github.com/nim-lang/Nim/pull/20222 --- compiler/nifgen.nim | 2 +- lib/system/comparisons.nim | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/nifgen.nim b/compiler/nifgen.nim index cf267ef14b..8467a93473 100644 --- a/compiler/nifgen.nim +++ b/compiler/nifgen.nim @@ -983,7 +983,7 @@ proc genericParamToNif(n: PNode; parent: PNode; c: var TranslationContext) = toNif n, parent, c proc addExternName(sym: PSym; c: var TranslationContext) = - if sym.loc.snippet != nil: + if sym.loc.snippet != "": c.b.addStrLit sym.loc.snippet else: c.b.addStrLit sym.name.s diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index a8d78bb93a..0a6ac150bb 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -38,6 +38,21 @@ proc `==`*[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.} proc `==`*[T: proc | iterator](x, y: T): bool {.magic: "EqProc", noSideEffect.} ## Checks that two `proc` variables refer to the same procedure. +when true: + # guard against string converted to cstring implicitly; see also #bug #25488 + proc isNil*(x: string): bool {.noSideEffect, error: "'isNil' is invalid for 'string'".} + + + # bug #9149; ensure that 'typeof(nil)' does not match *too* well by using 'typeof(nil) | typeof(nil)', + # especially for converters, see tests/overload/tconverter_to_string.nim + # Eventually we will be able to remove this hack completely. + + proc `==`*(x: string; y: typeof(nil) | typeof(nil)): bool {.error: "'nil' is invalid for 'string'".} = + discard + + proc `==`*(x: typeof(nil) | typeof(nil); y: string): bool {.error: "'nil' is invalid for 'string'".} = + discard + proc `<=`*[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.} proc `<=`*(x, y: string): bool {.magic: "LeStr", noSideEffect.} = ## Compares two strings and returns true if `x` is lexicographically From ae5f864bff5b83799424f404955c8affe34267ae Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:50:45 +0800 Subject: [PATCH 5/5] fixes #25494; [regression] Crash on enum ranges as default parameters in generic procs (#25496) fixes #25494; --- compiler/semexprs.nim | 8 ++++---- tests/generics/tgenerics_issues.nim | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 50d7860f35..38c904f439 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -813,7 +813,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp inc(lastIndex) if isGeneric: for i in 0..