From 5ad790cef3a865920cd2e54455844b209331904d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:32:14 +0800 Subject: [PATCH 1/2] fixes #25007; implements `setLenUninit` for refc --- compiler/ast.nim | 1 + compiler/ccgexprs.nim | 7 +++++-- compiler/condsyms.nim | 2 ++ compiler/jsgen.nim | 2 +- compiler/nifgen.nim | 1 + compiler/semdata.nim | 2 +- compiler/semmagic.nim | 2 +- compiler/vmgen.nim | 2 +- lib/system.nim | 16 ++++++++++++++++ lib/system/seqs_v2.nim | 2 +- lib/system/sysstr.nim | 40 ++++++++++++++++++++++++++++++++++++++++ tests/stdlib/tsystem.nim | 7 +++++++ 12 files changed, 77 insertions(+), 7 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 34b00eed92702..d1d2d127a0a64 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -500,6 +500,7 @@ type mAppendStrCh, mAppendStrStr, mAppendSeqElem, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, + mSetLengthSeqUninit, mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 26908a92eca82..4ca34b9d74e0f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2214,7 +2214,7 @@ proc isTrivialTypesToSnippet(t: PType): Snippet = else: result = NimTrue -proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = +proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc, noinit = false) = if optSeqDestructors in p.config.globalOptions: e[1] = makeAddr(e[1], p.module.idgen) genCall(p, e, d) @@ -2236,7 +2236,9 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = pExpr = cIfExpr(ra, cAddr(derefField(ra, "Sup")), NimNil) else: pExpr = ra - call.snippet = cCast(rt, cgCall(p, "setLengthSeqV2", pExpr, rti, rb, + + let name = if noinit: "setLengthSeqUninit" else: "setLengthSeqV2" + call.snippet = cCast(rt, cgCall(p, name, pExpr, rti, rb, isTrivialTypesToSnippet(t.skipTypes(abstractInst)[0]))) genAssignment(p, a, call, {}) @@ -2975,6 +2977,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimGCunref"), ra) of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthSeq: genSetLengthSeq(p, e, d) + of mSetLengthSeqUninit: genSetLengthSeq(p, e, d, noinit = true) of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet, mXorSet: genSetOp(p, e, d, op) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index adef5f364a29e..54b0ea49aff71 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -172,3 +172,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasDefaultFloatRoundtrip") defineSymbol("nimHasXorSet") + defineSymbol("nimHasSetLengthSeqUninitMagic") + diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 442d731a3e3aa..7ae649374044c 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2434,7 +2434,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = binaryExpr(p, n, r, "mnewString", """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); } else {$3.length = $4; }""") - of mSetLengthSeq: + of mSetLengthSeq, mSetLengthSeqUninit: var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) diff --git a/compiler/nifgen.nim b/compiler/nifgen.nim index 95e9815851713..cf267ef14bc5d 100644 --- a/compiler/nifgen.nim +++ b/compiler/nifgen.nim @@ -358,6 +358,7 @@ proc magicToNifTag(s: TMagic): (string, int) = of mExit: ("exit", NoMagic) of mSetLengthStr: ("setlenstr", NoMagic) of mSetLengthSeq: ("setlenseq", NoMagic) + of mSetLengthSeqUninit: ("setlensequninit", NoMagic) of mIsPartOf: ("ispartof", NoMagic) of mAstToStr: ("asttostr", NoMagic) of mParallel: ("parallel", NoMagic) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b31395ed5530e..e3be90014ee95 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -710,7 +710,7 @@ proc analyseIfAddressTakenInCall*(c: PContext, n: PNode, isConverter = false) = return const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, - mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap, + mSetLengthStr, mSetLengthSeq, mSetLengthSeqUninit, mAppendStrCh, mAppendStrStr, mSwap, mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved} template checkIfConverterCalled(c: PContext, n: PNode) = diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 0b7178357518f..0ad6117813a68 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -667,7 +667,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = semQuantifier(c, n) of mOld: result = semOld(c, n) - of mSetLengthSeq: + of mSetLengthSeq, mSetLengthSeqUninit: result = n let seqType = result[1].typ.skipTypes({tyPtr, tyRef, # in case we had auto-dereferencing tyVar, tyGenericInst, tyOwned, tySink, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 851cfa401dc1a..e365d3f236635 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1221,7 +1221,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMag var tmp = c.genx(n[1]) c.gABC(n, opcQuit, tmp) c.freeTemp(tmp) - of mSetLengthStr, mSetLengthSeq: + of mSetLengthStr, mSetLengthSeq, mSetLengthSeqUninit: unused(c, n, dest) var d = c.genx(n[1]) var tmp = c.genx(n[2]) diff --git a/lib/system.nim b/lib/system.nim index f81c6d5363a09..a18e81d3d7130 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -955,6 +955,22 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {. ## assert x == @[10] ## ``` +when defined(nimHasSetLengthSeqUninitMagic): + func setLenUninit*[T](s: var seq[T], newlen: Natural) {.magic: "SetLengthSeqUninit", nodestroy.} = + ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type. + ## New slots will not be initialized. + ## + ## If the current length is greater than the new length, + ## `s` will be truncated. + ## ```nim + ## var x = @[10, 20] + ## x.setLenUninit(5) + ## x[4] = 50 + ## assert x[4] == 50Add commentMore actions + ## x.setLenUninit(1) + ## assert x == @[10] + ## ``` + proc setLen*(s: var string, newlen: Natural) {. magic: "SetLengthStr", noSideEffect.} ## Sets the length of string `s` to `newlen`. diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 6ace66afeab3b..5d735a3fe60ee 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -202,7 +202,7 @@ func capacity*[T](self: seq[T]): int {.inline.} = let sek = cast[ptr NimSeqV2[T]](unsafeAddr self) result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0 -func setLenUninit*[T](s: var seq[T], newlen: Natural) {.nodestroy.} = +func setLenUninit[T](s: var seq[T], newlen: Natural) {.nodestroy.} = ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type. ## New slots will not be initialized. ## diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index b864da853188e..4fee6600337b8 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -300,6 +300,46 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericS zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) 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" + 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 + result.len = newLen + proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int, isTrivial: bool): PGenericSeq {. compilerRtl.} = sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq" diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim index 343021bd3da49..ba05cb4286518 100644 --- a/tests/stdlib/tsystem.nim +++ b/tests/stdlib/tsystem.nim @@ -236,5 +236,12 @@ proc bar2() = doAssert cstring(nil) <= cstring(nil) doAssert cstring("") <= cstring("") + var x = @[10, 20] + x.setLenUninit(5) + x[4] = 50 + doAssert x[4] == 50 + x.setLenUninit(1) + doAssert x == @[10] + static: bar2() bar2() From e01079818ba3a1f177dc5634bc48fdac326ad200 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:39:06 +0800 Subject: [PATCH 2/2] adds a changelog --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index e12265d1691e5..33f36deec2aaa 100644 --- a/changelog.md +++ b/changelog.md @@ -33,6 +33,8 @@ errors. - `strutils.multiReplace` overload for character set replacements in a single pass. Useful for string sanitation. Follows existing multiReplace semantics. +- `system.setLenUninit` now supports refc, JS and VM backends. + [//]: # "Changes:" - `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.