This commit is contained in:
araq
2026-03-07 18:11:40 +01:00
parent 6f2df832d4
commit 46c24ba3e7
3 changed files with 47 additions and 24 deletions

View File

@@ -962,7 +962,8 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, d, e, cDeref(rdLoc(a)), a.storage)
proc cowBracket(p: BProc; n: PNode) =
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions:
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and
not p.config.isDefined("nimsso"):
let strCandidate = n[0]
if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
var a: TLoc = initLocExpr(p, strCandidate)
@@ -987,6 +988,14 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
expr(p, e[0], d)
# bug #19497
d.lode = e
elif p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString:
# addr s[i] for nimsso: nimStrAtMutV3 returns char* directly — no extra & needed
var base = initLocExpr(p, e[0][0])
var idx = initLocExpr(p, e[0][1])
putIntoDest(p, d, e,
cCall(cgsymValue(p.module, "nimStrAtMutV3"), byRefLoc(p, base), rdLoc(idx)),
base.storage)
else:
var a: TLoc = initLocExpr(p, e[0])
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
@@ -1315,16 +1324,21 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.snippet = cDeref(a.snippet)
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
if p.config.isDefined("nimsso") and ty.kind == tyString:
# direct writes (s[i] = c) are intercepted in genAsgn via nimStrPutV3
let bra = byRefLoc(p, a)
putIntoDest(p, d, n,
subscript(cCall(cgsymValue(p.module, "nimStrData"), bra), rcb), a.storage)
if lfPrepareForMutation in d.flags:
# s[i] passed as `var char`: return *(nimStrAtMutV3(&s, i)) — a valid C lvalue
putIntoDest(p, d, n,
cDeref(cCall(cgsymValue(p.module, "nimStrAtMutV3"), bra, rcb)), a.storage)
else:
putIntoDest(p, d, n,
cCall(cgsymValue(p.module, "nimStrAtV3"), bra, rcb), a.storage)
else:
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"), bra)
let ra = rdLoc(a)
putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage)

View File

@@ -1940,6 +1940,15 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
elif optFieldCheck in p.options and isDiscriminantField(e[0]):
genLineDir(p, e)
asgnFieldDiscriminant(p, e)
elif p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString:
# nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally)
genLineDir(p, e)
var base = initLocExpr(p, e[0][0])
var idx = initLocExpr(p, e[0][1])
var rhs = initLocExpr(p, e[1])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimStrPutV3"),
byRefLoc(p, base), rdLoc(idx), rdCharLoc(rhs))
else:
let le = e[0]
let ri = e[1]

View File

@@ -74,7 +74,7 @@ template guts(s: SmallString): (int, ptr UncheckedArray[char]) =
else:
(slen, cast[ptr UncheckedArray[char]](addr s.payload[0]))
proc `[]`(s: SmallString; i: int): char {.inline.} =
proc nimStrAtV3*(s: var SmallString; i: int): char {.compilerproc, inline.} =
let slen = int s.slen
if slen <= PayloadSize:
# unchecked: when i >= 7 we store into the `more` overlay
@@ -84,7 +84,7 @@ proc `[]`(s: SmallString; i: int): char {.inline.} =
else:
result = s.more.data[i]
proc `[]=`(s: var SmallString; i: int; c: char) =
proc nimStrPutV3*(s: var SmallString; i: int; c: char) {.compilerproc, inline.} =
let slen = int s.slen
if slen <= PayloadSize:
# unchecked: when i >= 7 we store into the `more` overlay
@@ -96,7 +96,7 @@ proc `[]=`(s: var SmallString; i: int; c: char) =
if i < AlwaysAvail:
s.payload[i] = c
proc cmp*(a, b: SmallString): int =
proc cmp(a, b: SmallString): int =
# Use slen directly for prefix length: for short/medium it is the real length,
# for long it is the sentinel (> AlwaysAvail), so min(..., AlwaysAvail) still gives 7.
# This avoids dereferencing `more` before the prefix comparison.
@@ -116,7 +116,7 @@ proc cmp*(a, b: SmallString): int =
if result == 0:
result = la - lb
proc `==`*(a, b: SmallString): bool =
proc `==`(a, b: SmallString): bool =
if a.slen != b.slen: return false
# slen equal: for short/medium this means equal lengths; for long (both sentinel) we still need fullLen.
let slen = int(a.slen)
@@ -133,7 +133,7 @@ proc `==`*(a, b: SmallString): bool =
if la != b.more.fullLen: return false
cmpMem(addr a.more.data[pfxLen], addr b.more.data[pfxLen], la - pfxLen) == 0
proc `<=`*(a, b: SmallString): bool {.inline.} = cmp(a, b) <= 0
proc `<=`(a, b: SmallString): bool {.inline.} = cmp(a, b) <= 0
proc continuesWith*(s, sub: SmallString; start: int): bool =
if start < 0: return false
@@ -158,7 +158,7 @@ proc startsWith*(s, sub: SmallString): bool {.inline.} = continuesWith(s, sub, 0
proc endsWith*(s, sub: SmallString): bool {.inline.} = continuesWith(s, sub, s.len - sub.len)
proc add*(s: var SmallString; c: char) =
proc add(s: var SmallString; c: char) =
let slen = int(s.slen)
if slen <= PayloadSize:
let newLen = slen + 1
@@ -187,7 +187,7 @@ proc add*(s: var SmallString; c: char) =
s.more.data[l + 1] = '\0'
# l >= PayloadSize > AlwaysAvail, so prefix is unaffected
proc add*(s: var SmallString; t: SmallString) =
proc add(s: var SmallString; t: SmallString) =
let slen = int(s.slen)
let (tl, tp) = t.guts # fetch t's guts before any mutation (aliasing safety)
if tl == 0: return
@@ -223,11 +223,6 @@ proc add*(s: var SmallString; t: SmallString) =
s.more.data[newLen] = '\0'
# sl >= PayloadSize > AlwaysAvail, so prefix is unaffected
proc `&`*(a, b: SmallString): SmallString =
result = a
result.add(b)
{.push overflowChecks: off, rangeChecks: off.}
proc prepareAddLong(s: var SmallString; newLen: int) =
@@ -428,13 +423,18 @@ proc prepareMutation*(s: var string) {.inline.} =
{.cast(noSideEffect).}:
nimPrepareStrMutationV2(cast[ptr SmallString](addr s)[])
proc nimStrAtMutV3*(s: var SmallString; i: int): var char {.compilerproc, inline.} =
## Returns a mutable reference to the i-th char. Handles COW for long strings.
## Used by the codegen when s[i] is passed as a `var char` argument.
if int(s.slen) > PayloadSize:
nimPrepareStrMutationV2(s) # COW: ensure unique heap block before exposing ref
result = s.more.data[i]
else:
result = (cast[ptr UncheckedArray[char]](addr s.payload[0]))[i]
proc nimAddStrV1(s: var SmallString; src: SmallString) {.compilerRtl, inline.} =
s.add(src)
proc nimStrAtLe(s: SmallString; idx: int; ch: char): bool {.compilerRtl, inline.} =
let l = s.len
result = idx < l and s[idx] <= ch
func capacity*(self: SmallString): int {.inline.} =
## Returns the current capacity of the string.
let slen = int(self.slen)