From 9becd1453da64d75fb3decc48920669c02d3f82a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 14 Nov 2025 23:20:42 +0800 Subject: [PATCH 01/14] fixes #25284; `.global` initialization inside method hoisted to preInitProc (#25285) fixes #25284 ```nim proc m2() = let v {.global, used.}: string = f2(f2("123")) ``` transform lifted `.global`statements in the top level scope --- compiler/cgen.nim | 5 ++++- compiler/injectdestructors.nim | 4 ++-- tests/global/tglobal3.nim | 7 +++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 518613c1bd..1cf647978d 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -2449,7 +2449,10 @@ proc handleProcGlobals(m: BModule) = # fixes recursive calls #24997 swap stmts, m.preInitProc.s(cpsStmts) - genStmts(m.preInitProc, procGlobals[i]) + var transformedN = procGlobals[i] + if sfInjectDestructors in m.module.flags: + transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN) + genStmts(m.preInitProc, transformedN) swap stmts, m.preInitProc.s(cpsStmts) handleProcGlobals(m) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 223783a3f9..7fca0ab2e7 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -968,10 +968,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing {sfPure, sfGlobal} <= v.sym.flags and isInProc - let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {}) if isGlobalPragma: - c.graph.procGlobals.add value + c.graph.procGlobals.add n else: + let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {}) result.add value elif ri.kind == nkEmpty and c.inLoop > 0: let skipInit = v.kind == nkDotExpr and # Closure var diff --git a/tests/global/tglobal3.nim b/tests/global/tglobal3.nim index 80c1cd640d..b7f3f55391 100644 --- a/tests/global/tglobal3.nim +++ b/tests/global/tglobal3.nim @@ -55,3 +55,10 @@ block: # bug #24997 doAssert not isNil(u(typeof(B.j))) R() discard u(B) + +proc f2(str: string): string = str +proc m2() = + let v {.global, used.}: string = f2(f2("123")) + assert v == "123" + +m2() From 39be9b981d6608c9da33ca5c25118114eab121ea Mon Sep 17 00:00:00 2001 From: lit Date: Sat, 15 Nov 2025 01:43:13 +0800 Subject: [PATCH 02/14] fixes #25227; crash when codegen user-defined tuple iterate (#25228) fixes #25227 --- compiler/ccgexprs.nim | 2 +- compiler/jsgen.nim | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5859abd8b4..6b2a644099 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1006,7 +1006,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = var i: int = 0 var a: TLoc = initLocExpr(p, e[0]) - let tupType = a.t.skipTypes(abstractInst+{tyVar}) + let tupType = a.t.skipTypes(abstractInst+{tyVar}+tyUserTypeClasses) # ref #25227 assert tupType.kind == tyTuple d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index acd49110ab..c52b26e69b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1375,7 +1375,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyBaseIndex let b = if n.kind == nkHiddenAddr: n[0] else: n gen(p, b[0], a) - if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple: + if skipTypes(b[0].typ, abstractVarRange + tyTypeClasses).kind == tyTuple: + # ref #25227 about `+ tyTypeClasses` r.res = makeJSString("Field" & $getFieldPosition(p, b[1])) else: if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr") From 01c084077eb4e11e7cda743fd684f41ba3afccc4 Mon Sep 17 00:00:00 2001 From: Zoom Date: Sat, 15 Nov 2025 11:41:57 +0400 Subject: [PATCH 03/14] std: `sysstr` refactor (#25185) Continuation of #25180. This one refactors the sequence routines. Preparation for extending with new routines. Mostly removes repeating code to simplify debugging. Removes: - `incrSeqV2` superseded by `incrSeqV3`, - `setLengthSeq` superseded by `setLengthSeqV2` Note comment on line 338, acknowledging that implementation of `setLenUninit` from #25022 does zero the new memory in this branch, having been copied from `setLengthSeqV2`. This PR does not fix this. --- lib/system/sysstr.nim | 155 ++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 103 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 4fee660033..e866cbc284 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -252,14 +252,6 @@ proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compiler result.reserved = r inc(result.len) -proc incrSeqV2(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} = - # incrSeq version 2 - result = seq - if result.len >= result.space: - let r = resize(result.space) - result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r)) - result.reserved = r - proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} = if s == nil: result = cast[PGenericSeq](newSeq(typ, 1)) @@ -274,112 +266,68 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} = # since we steal the content from 's', it's crucial to set s's len to 0. s.len = 0 -proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericSeq {. - compilerRtl, inl.} = - result = seq - if result.space < newLen: - let r = max(resize(result.space), newLen) - result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r)) - result.reserved = r - elif newLen < result.len: - # we need to decref here, otherwise the GC leaks! - when not defined(boehmGC) and not defined(nogc) and - not defined(gcMarkAndSweep) and not defined(gogc) and - not defined(gcRegions): - if ntfNoRefs notin extGetCellType(result).base.flags: - for i in newLen..result.len-1: - forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waZctDecRef) +proc extendCapacityRaw(src: PGenericSeq; typ: PNimType; + elemSize, elemAlign, newLen: int): PGenericSeq {.inline.} = + ## Reallocs `src` to fit `newLen` elements without any checks. + ## Capacity always increases to at least next `resize` step. + let newCap = max(resize(src.space), newLen) + result = cast[PGenericSeq](newSeq(typ, newCap)) + copyMem(dataPointer(result, elemAlign), dataPointer(src, elemAlign), src.len * elemSize) + # since we steal the content from 's', it's crucial to set s's len to 0. + src.len = 0 - # XXX: zeroing out the memory can still result in crashes if a wiped-out - # cell is aliased by another pointer (ie proc parameter or a let variable). - # This is a tough problem, because even if we don't zeroMem here, in the - # presence of user defined destructors, the user will expect the cell to be - # "destroyed" thus creating the same problem. We can destroy the cell in the - # finalizer of the sequence, but this makes destruction non-deterministic. - zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) - result.len = newLen +proc truncateRaw(src: PGenericSeq; baseFlags: set[TNimTypeFlag]; isTrivial: bool; + elemSize, elemAlign, newLen: int): PGenericSeq {.inline.} = + ## Truncates `src` to `newLen` without any checks. + ## Does not set `src.len` + # sysAssert src.space > newlen + # sysAssert newLen < src.len + result = src + # we need to decref here, otherwise the GC leaks! + when not defined(boehmGC) and not defined(nogc) and + not defined(gcMarkAndSweep) and not defined(gogc) and + not defined(gcRegions): + if ntfNoRefs notin baseFlags: + for i in newLen.. s.space: + s.extendCapacityRaw(typ, elemSize, elemAlign, newLen) + elif newLen < s.len: + s.truncateRaw(typ.base.flags, isTrivial, elemSize, elemAlign, newLen) + else: + when doInit: + zeroMem(dataPointer(s, elemAlign, elemSize, s.len), (newLen-%s.len) *% elemSize) + s result.len = newLen +proc setLengthSeqUninit(s: PGenericSeq; typ: PNimType; newLen: int; isTrivial: bool): PGenericSeq {. + compilerRtl.} = + sysAssert typ.kind == tySequence, "setLengthSeqUninit: type is not a seq" + setLengthSeqImpl(s, typ, newLen, isTrivial, doInit = false) + proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int, isTrivial: bool): PGenericSeq {. compilerRtl.} = sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq" - if s == nil: - if newLen == 0: - result = s - else: - result = cast[PGenericSeq](newSeq(typ, newLen)) - else: - let elemSize = typ.base.size - let elemAlign = typ.base.align - if s.space < newLen: - let r = max(resize(s.space), newLen) - result = cast[PGenericSeq](newSeq(typ, r)) - copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize) - # since we steal the content from 's', it's crucial to set s's len to 0. - s.len = 0 - elif newLen < s.len: - result = s - # we need to decref here, otherwise the GC leaks! - when not defined(boehmGC) and not defined(nogc) and - not defined(gcMarkAndSweep) and not defined(gogc) and - not defined(gcRegions): - if ntfNoRefs notin typ.base.flags: - for i in newLen..result.len-1: - forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waZctDecRef) - - # XXX: zeroing out the memory can still result in crashes if a wiped-out - # cell is aliased by another pointer (ie proc parameter or a let variable). - # This is a tough problem, because even if we don't zeroMem here, in the - # presence of user defined destructors, the user will expect the cell to be - # "destroyed" thus creating the same problem. We can destroy the cell in the - # finalizer of the sequence, but this makes destruction non-deterministic. - if not isTrivial: # optimization for trivial types - zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) - else: - result = s - zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize) - result.len = newLen + setLengthSeqImpl(s, typ, newLen, isTrivial, doInit = true) func capacity*(self: string): int {.inline.} = ## Returns the current capacity of the string. @@ -402,3 +350,4 @@ func capacity*[T](self: seq[T]): int {.inline.} = let sek = cast[PGenericSeq](self) result = if sek != nil: sek.space else: 0 + From b539adf82958cd1696b920f5715df355ec5bc132 Mon Sep 17 00:00:00 2001 From: Zoom Date: Sat, 15 Nov 2025 11:42:10 +0400 Subject: [PATCH 04/14] std: `sysstr` cleanup, add docs (#25180) - Removed redundant `len` and `reserved` sets already performed by prior `rawNewStringNoInit` calls. - Reuse `appendChar` - Removed never used `newOwnedString` - Added internal `toOwnedCopy` - Documents differences in impls of internal procs used for `system.string.setLen`: + `strs_v2.setLengthStrV2`: - does not set the terminating zero byte when new length is 0 - does not handle negative new length + `sysstr.setLengthStr`: - sets the terminating zero byte when new length is 0 - bounds negative new length to 0 --- lib/system/strs_v2.nim | 4 ++ lib/system/sysstr.nim | 121 ++++++++++++++++++++++------------------- 2 files changed, 68 insertions(+), 57 deletions(-) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 95e76b1f8f..1b44e9123c 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -141,6 +141,10 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} = result = NimStringV2(len: len, p: p) proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} = + ## Sets the `s` length to `newLen` zeroing memory on growth. + ## Terminating zero at `s[newLen]` for cstring compatibility is set + ## on length change, **excluding** `newLen == 0`. + ## Negative `newLen` is **not** bound to zero. if newLen == 0: discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations" else: diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index e866cbc284..c84cb99b11 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -48,6 +48,8 @@ else: cast[NimString](newObjNoInit(addr(strDesc), size)) proc rawNewStringNoInit(space: int): NimString = + ## Returns a newly-allocated NimString with `reserved` set. + ## .. warning:: `len` and the terminating null-byte are not set! let s = max(space, 7) result = allocStrNoInit(sizeof(TGenericSeq) + s + 1) result.reserved = s @@ -55,11 +57,21 @@ proc rawNewStringNoInit(space: int): NimString = result.elemSize = 1 proc rawNewString(space: int): NimString {.compilerproc.} = + ## Returns a newly-allocated and *not* zeroed NimString + ## with everything required set: + ## - `reserved` + ## - `len` (0) + ## - terminating null-byte result = rawNewStringNoInit(space) result.len = 0 result.data[0] = '\0' proc mnewString(len: int): NimString {.compilerproc.} = + ## Returns a newly-allocated and zeroed NimString + ## with everything required set: + ## - `reserved` + ## - `len` + ## - terminating null-byte result = rawNewStringNoInit(len) result.len = len zeroMem(addr result.data[0], len + 1) @@ -91,29 +103,28 @@ proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} = copyMem(addr(result.data), str, len) result.data[len] = '\0' +proc toOwnedCopy(src: NimString): NimString {.inline.} = + ## Expects `src` to be not nil and initialized (len and terminating zero set) + result = rawNewStringNoInit(src.len) + result.len = src.len + copyMem(addr(result.data), addr(src.data), src.len + 1) + proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = if str == nil: NimString(nil) else: toNimStr(str, str.len) proc copyString(src: NimString): NimString {.compilerRtl.} = + ## Expects `src` to be initialized (len and terminating zero set) if src != nil: if (src.reserved and seqShallowFlag) != 0: result = src else: - result = rawNewStringNoInit(src.len) - result.len = src.len - copyMem(addr(result.data), addr(src.data), src.len + 1) + result = toOwnedCopy(src) sysAssert((seqShallowFlag and result.reserved) == 0, "copyString") when defined(nimShallowStrings): if (src.reserved and strlitFlag) != 0: result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag -proc newOwnedString(src: NimString; n: int): NimString = - result = rawNewStringNoInit(n) - result.len = n - copyMem(addr(result.data), addr(src.data), n) - result.data[n] = '\0' - proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = if src != nil: if (src.reserved and seqShallowFlag) != 0: @@ -129,10 +140,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = result.reserved = s when defined(gogc): result.elemSize = 1 + result.len = src.len + copyMem(addr(result.data), addr(src.data), src.len + 1) else: - result = rawNewStringNoInit(src.len) - result.len = src.len - copyMem(addr(result.data), addr(src.data), src.len + 1) + result = toOwnedCopy(src) sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1") when defined(nimShallowStrings): if (src.reserved and strlitFlag) != 0: @@ -140,28 +151,9 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = proc copyDeepString(src: NimString): NimString {.inline.} = if src != nil: - result = rawNewStringNoInit(src.len) - result.len = src.len - copyMem(addr(result.data), addr(src.data), src.len + 1) + result = toOwnedCopy(src) -proc addChar(s: NimString, c: char): NimString = - # is compilerproc! - if s == nil: - result = rawNewStringNoInit(1) - result.len = 0 - else: - result = s - if result.len >= result.space: - let r = resize(result.space) - result = rawNewStringNoInit(r) - result.len = s.len - copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) - result.reserved = r - result.data[result.len] = c - result.data[result.len+1] = '\0' - inc(result.len) - -# These routines should be used like following: +# The following resize- and append- routines should be used like following: # # s &= "Hello " & name & ", how do you feel?" # @@ -193,46 +185,61 @@ proc addChar(s: NimString, c: char): NimString = # s = rawNewString(0); proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = + ## Prepares `dest` for appending up to `addlen` new bytes. + ## .. warning:: Does not update `len`! if dest == nil: - result = rawNewString(addlen) - elif dest.len + addlen <= dest.space: + return rawNewString(addlen) + let futureLen = dest.len + addlen + if futureLen <= dest.space: result = dest else: # slow path: - let sp = max(resize(dest.space), dest.len + addlen) + # growth strategy: next `resize` step or exact `futureLen` if jumping over + let sp = max(resize(dest.space), futureLen) result = rawNewStringNoInit(sp) result.len = dest.len - copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1) - result.reserved = sp - #result = rawNewString(sp) - #copyMem(result, dest, dest.len + sizeof(TGenericSeq)) - # DO NOT UPDATE LEN YET: dest.len = newLen - -proc appendString(dest, src: NimString) {.compilerproc, inline.} = - if src != nil: - copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) - inc(dest.len, src.len) + # newFutureLen > space => addlen is never zero, copy terminating null anyway + copyMem(addr(result.data), addr(dest.data), dest.len + 1) proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = dest.data[dest.len] = c dest.data[dest.len+1] = '\0' inc(dest.len) -proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = - let n = max(newLen, 0) +proc addChar(s: NimString, c: char): NimString = + # is compilerproc! used in `ccgexprs.nim` if s == nil: - if n == 0: - return s - else: - result = mnewString(n) - elif n <= s.space: + result = rawNewStringNoInit(1) + result.len = 0 + else: result = s + if s.len >= s.space: # len.inc would overflow (`>` just in case) + let sp = resize(s.space) + result = rawNewStringNoInit(sp) + copyMem(addr(result.data), addr(s.data), s.len) + result.len = s.len + result.appendChar(c) + +proc appendString(dest, src: NimString) {.compilerproc, inline.} = + ## Raw, does not prepare `dest` space for copying + if src != nil: + copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) + inc(dest.len, src.len) + +proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = + ## Sets the `s` length to `newLen` zeroing memory on growth. + ## Terminating zero at `s[newLen]` for cstring compatibility is set + ## on any length change, including `newLen == 0`. + ## Negative `newLen` is bound to zero. + let n = max(newLen, 0) + if s == nil: # early return check + return if n == 0: s else: mnewString(n) # sets everything required + if n <= s.space: + result = s # len and null-byte still need updating else: let sp = max(resize(s.space), n) - result = rawNewStringNoInit(sp) - result.len = s.len - copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len) + result = rawNewStringNoInit(sp) # len and null-byte not set + copyMem(addr(result.data), addr(s.data), s.len) zeroMem(addr result.data[s.len], n - s.len) - result.reserved = sp result.len = n result.data[n] = '\0' From 7cb8165e75d37f81c1cdba2bf675834fbeaffbc7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:09:57 +0800 Subject: [PATCH 05/14] ref nightlies; Update NimonyStableCommit to a new version (#25289) https://github.com/nim-lang/nimony/commit/596ae916a8e8304e96aa1581410f765e5d6f1692 --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 62bda2d061..a54147680e 100644 --- a/koch.nim +++ b/koch.nim @@ -16,7 +16,7 @@ const ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1 SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01" - NimonyStableCommit = "3660f375dc0ec25da3401d3eb28603864340dc6d" # unversioned \ + NimonyStableCommit = "596ae916a8e8304e96aa1581410f765e5d6f1692" # unversioned \ # Note that Nimony uses Nim as a git submodule but we don't want to install # Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive # is **required** here. From 79ddb7d89e4320b448c35b5936ce150706bbf210 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sat, 15 Nov 2025 06:52:16 -0500 Subject: [PATCH 06/14] concept patch for `tyGenericInvocation` (#25288) matching between some generic invocations and equivalent instantiations did not have a code path --- compiler/concepts.nim | 10 ++++++++- tests/concepts/tconceptsv2.nim | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/compiler/concepts.nim b/compiler/concepts.nim index b808c6608b..7e547b19ce 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -137,7 +137,7 @@ proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable # check previously bound value if not matchType(c, old, value, m): return false - elif key.hasElementType and key.elementType.kind != tyNone: + elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone: # check constaint if matchType(c, unrollGenericParam(key), value, m) == false: return false @@ -358,6 +358,14 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool = if not matchType(c, f[i], ea[i], m): result = false break + elif f.kind == tyGenericInvocation: + # bind potential generic constraints into body + let body = f.base + for i in 1 ..< len(f): + bindParam(c,m,body[i-1], f[i]) + result = matchType(c, body, a, m) + else: # tyGenericInst + result = matchType(c, f.last, a, m) of tyOrdinal: result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam of tyStatic: diff --git a/tests/concepts/tconceptsv2.nim b/tests/concepts/tconceptsv2.nim index c735aeeacc..629ac1c876 100644 --- a/tests/concepts/tconceptsv2.nim +++ b/tests/concepts/tconceptsv2.nim @@ -546,3 +546,42 @@ proc len[T](t: DummyIndexable[T]): int = let dummyIndexable = DummyIndexable(@[1, 2]) echoAll(dummyIndexable) + +block: + type + C = concept + proc a(x: Self, i: int) + AObj[T] = object + x: T + ARef[T] = ref AObj[T] + + proc a[T: int](x: ARef[T], i: int) = + discard + + assert (ref AObj[int]) is C + +block: + type + C = concept + proc a(x: Self, i: int) + AObj[T; B] = object + x: T + ARef[T; B] = ref AObj[T,B] + + proc a[T: int, C: float](x: ARef[T, C], i: int) = + discard + + assert (ref AObj[int, int]) isnot C + assert (ref AObj[int, float]) is C + +block: + type + C = concept + proc a(x: Self, i: int) + AObj[T] = object + ARef[T] = ref AObj[T] + + proc a(x: ARef, i: int) = + discard + + assert (ref AObj[int]) is C From cd69f37f3a4fb46468b77b84ccf6aa3225c8895e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:06:28 +0800 Subject: [PATCH 07/14] Update NimonyStableCommit hash (#25292) ref https://github.com/nim-lang/nimony/commit/322178d9af6676363d5237382c6d6c1b4e56d3cd I will add an i386 CI for bootstrapping later --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index a54147680e..58df9fadb7 100644 --- a/koch.nim +++ b/koch.nim @@ -16,7 +16,7 @@ const ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1 SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01" - NimonyStableCommit = "596ae916a8e8304e96aa1581410f765e5d6f1692" # unversioned \ + NimonyStableCommit = "322178d9af6676363d5237382c6d6c1b4e56d3cd" # unversioned \ # Note that Nimony uses Nim as a git submodule but we don't want to install # Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive # is **required** here. From 46d4079357aacfdc909a59bded55e1fea510c74b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 18 Nov 2025 20:03:34 +0800 Subject: [PATCH 08/14] use `nimKochBootstrap` for niminst (#25293) so that `nimony` won't be required for nightlies. It's annoying to build `nimony` on each platform, e.g. `std/memfiles` which is used by `nimony` is not supported by `nintendoswitch` ``` bin/nim compile -f --incremental:off --compileonly --gen_mapping --cc:gcc --skipUserCfg --os:nintendoswitch --cpu:arm64 -d:danger -d:gitHash:cd69f37f3a4fb46468b77b84ccf6aa3225c8895e compiler/nim.nim ``` ``` /home/runner/work/nightlies/nightlies/nim/lib/pure/memfiles.nim(107, 40) Error: undeclared identifier: 'MAP_SHARED' candidates (edit distance, scope distance); see '--spellSuggest': (4, 5): 'freeShared' ``` --- tools/niminst/niminst.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 5244536099..4f4d7adfc4 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -544,7 +544,7 @@ proc srcdist(c: var ConfigData) = var dir = getOutputDir(c) / buildDir(osA, cpuA) if dirExists(dir): removeDir(dir) createDir(dir) - var cmd = ("$# compile -f --incremental:off --compileonly " & + var cmd = ("$# compile -f --incremental:off --d:nimKochBootstrap --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & " --os:$# --cpu:$# $# $#") % [findNim(), osname, cpuname, c.nimArgs, c.mainfile] From c308363015daaf134b34dfc94b5b5f079638d824 Mon Sep 17 00:00:00 2001 From: araq Date: Tue, 18 Nov 2025 17:05:48 +0100 Subject: [PATCH 09/14] fixed merge conflict --- lib/std/private/digitsutils.nim | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index f2d0d25cba..b6d2d10b97 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -29,18 +29,23 @@ const # doAssert res == digits100 # ``` -proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = +{.push checks: off, stackTrace: off.} + +when not defined(nimHasEnforceNoRaises): + {.pragma: enforceNoRaises.} + +proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline, enforceNoRaises.} = buf[pos] = digits100[2 * digits] buf[pos+1] = digits100[2 * digits + 1] #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) -proc trailingZeros2Digits*(digits: uint32): int {.inline.} = +proc trailingZeros2Digits*(digits: uint32): int {.inline, enforceNoRaises.} = trailingZeros100[digits] when defined(js): proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".} -func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} = +func addChars[T](result: var string, x: T, start: int, n: int) {.inline, enforceNoRaises.} = let old = result.len result.setLen old + n template impl = @@ -52,10 +57,10 @@ func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} = {.noSideEffect.}: copyMem result[old].addr, x[start].unsafeAddr, n -func addChars[T](result: var string, x: T) {.inline.} = +func addChars[T](result: var string, x: T) {.inline, enforceNoRaises.} = addChars(result, x, 0, x.len) -func addIntImpl(result: var string, x: uint64) {.inline.} = +func addIntImpl(result: var string, x: uint64) {.inline, enforceNoRaises.} = var tmp {.noinit.}: array[24, char] var num = x var next = tmp.len - 1 @@ -79,8 +84,6 @@ func addIntImpl(result: var string, x: uint64) {.inline.} = dec next addChars(result, tmp, next, tmp.len - next) -when not defined(nimHasEnforceNoRaises): - {.pragma: enforceNoRaises.} func addInt*(result: var string, x: uint64) {.enforceNoRaises.} = when nimvm: addIntImpl(result, x) From fff0201c5fe95715f40c5cbda1972b1e43d35d45 Mon Sep 17 00:00:00 2001 From: araq Date: Tue, 18 Nov 2025 17:06:52 +0100 Subject: [PATCH 10/14] system.nim: refactoring that removes cyclic dependencies in prep for IC --- lib/std/assertions.nim | 4 + lib/std/private/miscdollars.nim | 8 +- lib/system.nim | 230 +++++++++++++++++--------------- lib/system/excpt.nim | 54 ++++++-- lib/system/gc_interface.nim | 2 - lib/system/memalloc.nim | 10 +- lib/system/strs_v2.nim | 4 + 7 files changed, 182 insertions(+), 130 deletions(-) diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 56c37d2057..f31a8465af 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -19,12 +19,16 @@ import std/private/miscdollars type InstantiationInfo = tuple[filename: string, line: int, column: int] +{.push overflowChecks: off, rangeChecks: off.} + proc `$`(info: InstantiationInfo): string = # The +1 is needed here # instead of overriding `$` (and changing its meaning), consider explicit name. result = "" result.toLocation(info.filename, info.line, info.column + 1) +{.pop.} + # --------------------------------------------------------------------------- diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim index 06fda6fa1a..77ba158b0c 100644 --- a/lib/std/private/miscdollars.nim +++ b/lib/std/private/miscdollars.nim @@ -4,7 +4,13 @@ template toLocation*(result: var string, file: string | cstring, line: int, col: ## avoids spurious allocations # Hopefully this can be re-used everywhere so that if a user needs to customize, # it can be done in a single place. - result.add file + when file is cstring: + var i = 0 + while file[i] != '\0': + add(result, file[i]) + inc i + else: + result.add file if line > 0: result.add "(" addInt(result, line) diff --git a/lib/system.nim b/lib/system.nim index fece232b34..e6d0bd0bed 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1766,6 +1766,111 @@ template newException*(exceptn: typedesc, message: string; ## to `message`. Returns the new exception object. (ref exceptn)(msg: message, parent: parentException) +# we have to compute this here before turning it off in except.nim anyway ... +const NimStackTrace = compileOption("stacktrace") +const + usesDestructors = defined(gcDestructors) or defined(gcHooks) + +when notJSnotNims: + proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.} + ## Allows you to override the behaviour of your application when CTRL+C + ## is pressed. Only one such hook is supported. + ## + ## The handler runs inside a C signal handler and comes with similar + ## limitations. + ## + ## Allocating memory and interacting with most system calls, including using + ## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined + ## behavior and will likely lead to application crashes. + ## + ## The OS may call the ctrl-c handler from any thread, including threads + ## that were not created by Nim, such as happens on Windows. + ## + ## ## Example: + ## + ## ```nim + ## var stop: Atomic[bool] + ## proc ctrlc() {.noconv.} = + ## # Using atomics types is safe! + ## stop.store(true) + ## + ## setControlCHook(ctrlc) + ## + ## while not stop.load(): + ## echo "Still running.." + ## sleep(1000) + ## ``` + + when not defined(noSignalHandler) and not defined(useNimRtl): + proc unsetControlCHook*() + ## Reverts a call to setControlCHook. + + when hostOS != "standalone": + proc getStackTrace*(): string {.gcsafe.} + ## Gets the current stack trace. This only works for debug builds. + + proc getStackTrace*(e: ref Exception): string {.gcsafe.} + ## Gets the stack trace associated with `e`, which is the stack that + ## lead to the `raise` statement. This only works for debug builds. + + var + globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.} + ## With this hook you can influence exception handling on a global level. + ## If not nil, every 'raise' statement ends up calling this hook. + ## + ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this. + ## + ## If `globalRaiseHook` returns false, the exception is caught and does + ## not propagate further through the call stack. + + localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.} + ## With this hook you can influence exception handling on a + ## thread local level. + ## If not nil, every 'raise' statement ends up calling this hook. + ## + ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this. + ## + ## If `localRaiseHook` returns false, the exception + ## is caught and does not propagate further through the call stack. + + outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].} + ## Set this variable to provide a procedure that should be called + ## in case of an `out of memory`:idx: event. The standard handler + ## writes an error message and terminates the program. + ## + ## `outOfMemHook` can be used to raise an exception in case of OOM like so: + ## + ## ```nim + ## var gOutOfMem: ref EOutOfMemory + ## new(gOutOfMem) # need to be allocated *before* OOM really happened! + ## gOutOfMem.msg = "out of memory" + ## + ## proc handleOOM() = + ## raise gOutOfMem + ## + ## system.outOfMemHook = handleOOM + ## ``` + ## + ## If the handler does not raise an exception, ordinary control flow + ## continues and the program is terminated. + + unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].} + ## Set this variable to provide a procedure that should be called + ## in case of an `unhandle exception` event. The standard handler + ## writes an error message and terminates the program, except when + ## using `--os:any` + + {.push stackTrace: off, profiler: off.} + when defined(memtracker): + include "system/memtracker" + + when hostOS == "standalone": + include "system/embedded" + else: + include "system/excpt" + {.pop.} + + when not defined(nimPreviewSlimSystem): import std/assertions export assertions @@ -1844,9 +1949,6 @@ proc `<`*[T: tuple](x, y: T): bool = include "system/gc_interface" -# we have to compute this here before turning it off in except.nim anyway ... -const NimStackTrace = compileOption("stacktrace") - import system/coro_detection {.push checks: off.} @@ -1855,53 +1957,6 @@ import system/coro_detection # however, stack-traces are available for most parts # of the code -when notJSnotNims: - var - globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.} - ## With this hook you can influence exception handling on a global level. - ## If not nil, every 'raise' statement ends up calling this hook. - ## - ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this. - ## - ## If `globalRaiseHook` returns false, the exception is caught and does - ## not propagate further through the call stack. - - localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.} - ## With this hook you can influence exception handling on a - ## thread local level. - ## If not nil, every 'raise' statement ends up calling this hook. - ## - ## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this. - ## - ## If `localRaiseHook` returns false, the exception - ## is caught and does not propagate further through the call stack. - - outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].} - ## Set this variable to provide a procedure that should be called - ## in case of an `out of memory`:idx: event. The standard handler - ## writes an error message and terminates the program. - ## - ## `outOfMemHook` can be used to raise an exception in case of OOM like so: - ## - ## ```nim - ## var gOutOfMem: ref EOutOfMemory - ## new(gOutOfMem) # need to be allocated *before* OOM really happened! - ## gOutOfMem.msg = "out of memory" - ## - ## proc handleOOM() = - ## raise gOutOfMem - ## - ## system.outOfMemHook = handleOOM - ## ``` - ## - ## If the handler does not raise an exception, ordinary control flow - ## continues and the program is terminated. - unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].} - ## Set this variable to provide a procedure that should be called - ## in case of an `unhandle exception` event. The standard handler - ## writes an error message and terminates the program, except when - ## using `--os:any` - when defined(js) or defined(nimdoc): proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = ## Appends `y` to `x` in place. @@ -2028,6 +2083,16 @@ template unlikely*(val: bool): bool = import system/dollars export dollars +when notJSnotNims: + {.push stackTrace: off, profiler: off.} + + include "system/chcks" + + # we cannot compile this with stack tracing on + # as it would recurse endlessly! + include "system/integerops" + {.pop.} + when defined(nimAuditDelete): {.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".} else: @@ -2110,17 +2175,17 @@ when notJSnotNims: nimZeroMem(p, size) when declared(memTrackerOp): memTrackerOp("zeroMem", p, size) - proc copyMem(dest, source: pointer, size: Natural) = + proc copyMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} = nimCopyMem(dest, source, size) when declared(memTrackerOp): memTrackerOp("copyMem", dest, size) - proc moveMem(dest, source: pointer, size: Natural) = + proc moveMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} = c_memmove(dest, source, csize_t(size)) when declared(memTrackerOp): memTrackerOp("moveMem", dest, size) - proc equalMem(a, b: pointer, size: Natural): bool = + proc equalMem(a, b: pointer, size: Natural): bool {.enforceNoRaises.} = nimCmpMem(a, b, size) == 0 - proc cmpMem(a, b: pointer, size: Natural): int = + proc cmpMem(a, b: pointer, size: Natural): int {.enforceNoRaises.} = nimCmpMem(a, b, size).int when not defined(js) or defined(nimscript): @@ -2194,63 +2259,6 @@ when not defined(js): when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom() when declared(initGC): initGC() -when notJSnotNims: - proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.} - ## Allows you to override the behaviour of your application when CTRL+C - ## is pressed. Only one such hook is supported. - ## - ## The handler runs inside a C signal handler and comes with similar - ## limitations. - ## - ## Allocating memory and interacting with most system calls, including using - ## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined - ## behavior and will likely lead to application crashes. - ## - ## The OS may call the ctrl-c handler from any thread, including threads - ## that were not created by Nim, such as happens on Windows. - ## - ## ## Example: - ## - ## ```nim - ## var stop: Atomic[bool] - ## proc ctrlc() {.noconv.} = - ## # Using atomics types is safe! - ## stop.store(true) - ## - ## setControlCHook(ctrlc) - ## - ## while not stop.load(): - ## echo "Still running.." - ## sleep(1000) - ## ``` - - when not defined(noSignalHandler) and not defined(useNimRtl): - proc unsetControlCHook*() - ## Reverts a call to setControlCHook. - - when hostOS != "standalone": - proc getStackTrace*(): string {.gcsafe.} - ## Gets the current stack trace. This only works for debug builds. - - proc getStackTrace*(e: ref Exception): string {.gcsafe.} - ## Gets the stack trace associated with `e`, which is the stack that - ## lead to the `raise` statement. This only works for debug builds. - - {.push stackTrace: off, profiler: off.} - when defined(memtracker): - include "system/memtracker" - - when hostOS == "standalone": - include "system/embedded" - else: - include "system/excpt" - include "system/chcks" - - # we cannot compile this with stack tracing on - # as it would recurse endlessly! - include "system/integerops" - {.pop.} - when not defined(js): # this is a hack: without this when statement, you would get: diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 511839914f..18ebc1c609 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -42,11 +42,13 @@ proc writeToStdErr(msg: string) {.inline.} = # fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring writeToStdErr(msg.cstring, msg.len) +proc cstrToStrBuiltin(x: cstring): string {.magic: "CStrToStr", noSideEffect.} + proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} = var toWrite = true if errorMessageWriter != nil: try: - errorMessageWriter($data) + errorMessageWriter(cstrToStrBuiltin data) toWrite = false except: discard @@ -261,7 +263,10 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) = var oldLen = s.len s.toLocation(f.filename, f.line, 0) for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') - add(s, f.procname) + var i = 0 + while f.procname[i] != '\0': + add(s, f.procname[i]) + inc i when NimStackTraceMsgs: when typeof(f) is StackTraceEntry: add(s, f.frameMsg) @@ -283,8 +288,34 @@ proc `$`(stackTraceEntries: seq[StackTraceEntry]): string = else: addFrameEntry(result, s[i]) when hasSomeStackTrace: + const + Ten = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] - proc auxWriteStackTrace(f: PFrame, s: var string) = + proc i2s(x: int64): string = + # quick reimplementation; optimized for code size, no dependencies + if x < 0: + if x == -9223372036854775808: + result = "-9223372036854775808" + else: + result = "-" & i2s(0-x) + elif x < 10: + result = Ten[int x] # saves allocations + else: + var y = x + while true: + result.add char((y mod 10) + int('0')) + y = y div 10 + if y == 0: break + let last = result.len-1 + var i = 0 + let b = result.len div 2 + while i < b: + let ch = result[i] + result[i] = result[last-i] + result[last-i] = ch + inc i + + proc auxWriteStackTrace(f: PFrame, s: var string) {.raises: [].} = when hasThreadSupport: var tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar @@ -322,14 +353,14 @@ when hasSomeStackTrace: for j in countdown(i-1, 0): if tempFrames[j] == nil: add(s, "(") - add(s, $skipped) + s.add(i2s(skipped)) add(s, " calls omitted) ...\n") else: addFrameEntry(s, tempFrames[j]) proc stackTraceAvailable*(): bool - proc rawWriteStackTrace(s: var string) = + proc rawWriteStackTrace(s: var string) {.raises: [].} = when defined(nimStackTraceOverride): add(s, "Traceback (most recent call last, using override)\n") auxWriteStackTraceWithOverride(s) @@ -388,7 +419,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") - add(buf, $e.name) + add(buf, cstrToStrBuiltin(e.name)) add(buf, "]\n") if onUnhandledException != nil: @@ -408,7 +439,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = var buf: array[0..2000, char] var L = 0 if e.trace.len != 0: - var trace = $e.trace + var trace = cstrToStrBuiltin(e.trace) add(buf, trace) {.gcsafe.}: `=destroy`(trace) @@ -559,10 +590,11 @@ const nimCallDepthLimit {.intdefine.} = 2000 proc callDepthLimitReached() {.noinline.} = writeStackTrace() - let msg = "Error: call depth limit reached in a debug build (" & - $nimCallDepthLimit & " function calls). You can change it with " & - "-d:nimCallDepthLimit= but really try to avoid deep " & - "recursions instead.\n" + var msg = "Error: call depth limit reached in a debug build (" + msg.add(i2s(nimCallDepthLimit)) + msg.add(" function calls). You can change it with " & + "-d:nimCallDepthLimit= but really try to avoid deep " & + "recursions instead.\n") showErrorMessage2(msg) rawQuit(1) diff --git a/lib/system/gc_interface.nim b/lib/system/gc_interface.nim index 4540db21f2..b34ce4a566 100644 --- a/lib/system/gc_interface.nim +++ b/lib/system/gc_interface.nim @@ -1,6 +1,4 @@ # ----------------- GC interface --------------------------------------------- -const - usesDestructors = defined(gcDestructors) or defined(gcHooks) when not usesDestructors: {.pragma: nodestroy.} diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index 6347357347..b26f3af24d 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -1,13 +1,13 @@ when notJSnotNims: proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect, - tags: [], raises: [].} + tags: [], raises: [], enforceNoRaises.} ## Overwrites the contents of the memory at `p` with the value 0. ## ## Exactly `size` bytes will be overwritten. Like any procedure ## dealing with raw memory this is **unsafe**. proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign, - tags: [], raises: [].} + tags: [], raises: [], enforceNoRaises.} ## Copies the contents from the memory at `source` to the memory ## at `dest`. ## Exactly `size` bytes will be copied. The memory @@ -15,7 +15,7 @@ when notJSnotNims: ## memory this is **unsafe**. proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign, - tags: [], raises: [].} + tags: [], raises: [], enforceNoRaises.} ## Copies the contents from the memory at `source` to the memory ## at `dest`. ## @@ -25,7 +25,7 @@ when notJSnotNims: ## dealing with raw memory this is still **unsafe**, though. proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, - tags: [], raises: [].} + tags: [], raises: [], enforceNoRaises.} ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## @@ -34,7 +34,7 @@ when notJSnotNims: ## **unsafe**. proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect, - tags: [], raises: [].} + tags: [], raises: [], enforceNoRaises.} ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 1b44e9123c..1bc8fb7d78 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -23,6 +23,8 @@ type const nimStrVersion {.core.} = 2 +{.push overflowChecks: off, rangeChecks: off.} + template isLiteral(s): bool = (s.p == nil) or (s.p.cap and strlitFlag) == strlitFlag template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase) @@ -227,3 +229,5 @@ func capacity*(self: string): int {.inline.} = let str = cast[ptr NimStringV2](unsafeAddr self) result = if str.p != nil: str.p.cap and not strlitFlag else: 0 + +{.pop.} From 62943390eb8d7756320c6018e272310f06052e5f Mon Sep 17 00:00:00 2001 From: araq Date: Tue, 18 Nov 2025 17:12:53 +0100 Subject: [PATCH 11/14] progress --- lib/system/excpt.nim | 57 +++++++++++++++++++-------------------- lib/system/threadimpl.nim | 2 +- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 18ebc1c609..f9e6ec9a85 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -287,34 +287,34 @@ proc `$`(stackTraceEntries: seq[StackTraceEntry]): string = elif s[i].line == reraisedFromEnd: result.add "]]\n" else: addFrameEntry(result, s[i]) -when hasSomeStackTrace: - const - Ten = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] +const + Ten = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] - proc i2s(x: int64): string = - # quick reimplementation; optimized for code size, no dependencies - if x < 0: - if x == -9223372036854775808: - result = "-9223372036854775808" - else: - result = "-" & i2s(0-x) - elif x < 10: - result = Ten[int x] # saves allocations +proc i2s(x: int64): string = + # quick reimplementation; optimized for code size, no dependencies + if x < 0: + if x == -9223372036854775808: + result = "-9223372036854775808" else: - var y = x - while true: - result.add char((y mod 10) + int('0')) - y = y div 10 - if y == 0: break - let last = result.len-1 - var i = 0 - let b = result.len div 2 - while i < b: - let ch = result[i] - result[i] = result[last-i] - result[last-i] = ch - inc i + result = "-" & i2s(0-x) + elif x < 10: + result = Ten[int x] # saves allocations + else: + var y = x + while true: + result.add char((y mod 10) + int('0')) + y = y div 10 + if y == 0: break + let last = result.len-1 + var i = 0 + let b = result.len div 2 + while i < b: + let ch = result[i] + result[i] = result[last-i] + result[last-i] = ch + inc i +when hasSomeStackTrace: proc auxWriteStackTrace(f: PFrame, s: var string) {.raises: [].} = when hasThreadSupport: var @@ -439,7 +439,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = var buf: array[0..2000, char] var L = 0 if e.trace.len != 0: - var trace = cstrToStrBuiltin(e.trace) + var trace = $e.trace add(buf, trace) {.gcsafe.}: `=destroy`(trace) @@ -449,7 +449,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = xadd(buf, e.name, e.name.len) add(buf, "]\n") if onUnhandledException != nil: - onUnhandledException($cast[cstring](buf.addr)) + onUnhandledException(cstrToStrBuiltin(cast[cstring](buf.addr))) else: showErrorMessage(cast[cstring](buf.addr), L) @@ -546,8 +546,7 @@ proc reraiseException() {.compilerRtl.} = else: raiseExceptionAux(currException) -proc threadTrouble() = - # also forward declared, it is 'raises: []' hence the try-except. +proc threadTrouble() {.raises: [], gcsafe.} = try: if currException != nil: reportUnhandledError(currException) except: diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim index 093a920a1d..dcd1b267a0 100644 --- a/lib/system/threadimpl.nim +++ b/lib/system/threadimpl.nim @@ -2,7 +2,7 @@ var nimThreadDestructionHandlers* {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}] when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): proc deallocOsPages() {.rtl, raises: [].} -proc threadTrouble() {.raises: [], gcsafe.} + # create for the main thread. Note: do not insert this data into the list # of all threads; it's not to be stopped etc. when not defined(useNimRtl): From c82697251e802afea3f82bf7794897e6e1aae4ba Mon Sep 17 00:00:00 2001 From: araq Date: Tue, 18 Nov 2025 17:41:26 +0100 Subject: [PATCH 12/14] and again --- lib/system.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index e6d0bd0bed..b1774b965f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1754,6 +1754,15 @@ type when NimStackTraceMsgs: frameMsgLen*: int ## end position in frameMsgBuf for this frame. +when notJSnotNims and not gotoBasedExceptions: + type + PSafePoint = ptr TSafePoint + TSafePoint {.compilerproc, final.} = object + prev: PSafePoint # points to next safe point ON THE STACK + status: int + context: C_JmpBuf + SafePoint = TSafePoint + when defined(nimV2): var framePtr {.threadvar.}: PFrame @@ -2238,15 +2247,6 @@ when not defined(js) and declared(alloc0) and declared(dealloc): inc(i) dealloc(a) -when notJSnotNims and not gotoBasedExceptions: - type - PSafePoint = ptr TSafePoint - TSafePoint {.compilerproc, final.} = object - prev: PSafePoint # points to next safe point ON THE STACK - status: int - context: C_JmpBuf - SafePoint = TSafePoint - when not defined(js): when hasThreadSupport: when hostOS != "standalone": From 76a2b884f7f40723d3a2fd54879ca1d7308869c2 Mon Sep 17 00:00:00 2001 From: araq Date: Tue, 18 Nov 2025 18:35:43 +0100 Subject: [PATCH 13/14] and again --- lib/system/excpt.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index f9e6ec9a85..ea0eb13c81 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -632,7 +632,7 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and {.emit: "#endif".} except Exception: msg = currException.getStackTrace() & "Error: unhandled exception: " & - currException.msg & " [" & $currException.name & "]" + currException.msg & " [" & cstrToStrBuiltin(currException.name) & "]" except StdException as e: msg = "Error: unhandled cpp exception: " & $e.what() except: From 6de6de980a136a124d9408ac97af5418b45a9bb8 Mon Sep 17 00:00:00 2001 From: araq Date: Tue, 18 Nov 2025 18:39:13 +0100 Subject: [PATCH 14/14] progress --- lib/system/memory.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system/memory.nim b/lib/system/memory.nim index 156773c484..c6c3cb3ab0 100644 --- a/lib/system/memory.nim +++ b/lib/system/memory.nim @@ -5,7 +5,7 @@ const useLibC = not defined(nimNoLibc) when useLibC: import ansi_c -proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, compilerproc, inline.} = +proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, compilerproc, inline, enforceNoRaises.} = when useLibC: c_memcpy(dest, source, cast[csize_t](size)) else: @@ -16,7 +16,7 @@ proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, compiler d[i] = s[i] inc i -proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline.} = +proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline, enforceNoRaises.} = when useLibC: c_memset(a, v, cast[csize_t](size)) else: @@ -27,10 +27,10 @@ proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline.} = a[i] = v inc i -proc nimZeroMem*(p: pointer, size: Natural) {.compilerproc, nonReloadable, inline.} = +proc nimZeroMem*(p: pointer, size: Natural) {.compilerproc, nonReloadable, inline, enforceNoRaises.} = nimSetMem(p, 0, size) -proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadable, inline.} = +proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadable, inline, enforceNoRaises.} = when useLibC: c_memcmp(a, b, cast[csize_t](size)) else: @@ -42,7 +42,7 @@ proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadabl if d != 0: return d inc i -proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} = +proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline, enforceNoRaises.} = if a.isNil: return 0 when useLibC: cast[int](c_strlen(a))