Compare commits

..

1 Commits

Author SHA1 Message Date
ringabout
c38fab3576 test 2026-03-05 16:11:11 +08:00
102 changed files with 460 additions and 3486 deletions

View File

@@ -109,7 +109,7 @@ jobs:
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: crazy-max/ghaction-github-pages@v5
uses: crazy-max/ghaction-github-pages@v4
with:
build_dir: doc/html
env:

View File

@@ -1196,12 +1196,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tyString, tySet, tyDistinct}:
if o2.state == Sealed:
# During the original compilation, propagateToOwner set tfHasAsgn/tfHasOwned on the type before it was sealed
# On IC reload, the sealed type already has those flags
assert mask <= o2.flags, "IC bug: sealed type missing propagated flags"
else:
o2.incl mask
o2.incl mask
owner.incl mask
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
@@ -1275,8 +1270,7 @@ template transitionSymKindCommon*(k: TSymKind) =
s[] = TSym(kindImpl: k, itemId: obj.itemId, magicImpl: obj.magicImpl, typImpl: obj.typImpl, name: obj.name,
infoImpl: obj.infoImpl, ownerFieldImpl: obj.ownerFieldImpl, flagsImpl: obj.flagsImpl, astImpl: obj.astImpl,
optionsImpl: obj.optionsImpl, positionImpl: obj.positionImpl, offsetImpl: obj.offsetImpl,
disamb: obj.disamb, locImpl: obj.locImpl, annexImpl: obj.annexImpl, constraintImpl: obj.constraintImpl,
instantiatedFromImpl: obj.instantiatedFromImpl)
locImpl: obj.locImpl, annexImpl: obj.annexImpl, constraintImpl: obj.constraintImpl)
when hasFFI:
s.cnameImpl = obj.cnameImpl
when defined(nimsuggest):

View File

@@ -56,12 +56,12 @@ proc toConverterIndexEntry*(config: ConfigRef; converterSym: PSym): (nifstreams.
# Fallback: return empty entry
result = (nifstreams.SymId(0), nifstreams.SymId(0))
proc toMethodIndexEntry*(config: ConfigRef; methodSym: PSym; signature: string): (nifstreams.SymId, nifstreams.StrId) =
## Converts a method symbol/signature to a method index entry.
proc toMethodIndexEntry*(config: ConfigRef; methodSym: PSym; signature: string): MethodIndexEntry =
## Converts a method symbol to a MethodIndexEntry.
let methodSymName = methodSym.name.s & "." & $methodSym.disamb & "." & cachedModuleSuffix(config, methodSym.itemId.module.FileIndex)
result = (
pool.syms.getOrIncl(methodSymName),
pool.strings.getOrIncl(signature)
result = MethodIndexEntry(
fn: pool.syms.getOrIncl(methodSymName),
signature: pool.strings.getOrIncl(signature)
)
proc toClassSymId*(config: ConfigRef; typeId: ItemId): nifstreams.SymId =
@@ -167,8 +167,7 @@ const
proc isLocalSym(sym: PSym): bool {.inline.} =
sym.kindImpl in skLocalSymKinds or
(sym.kindImpl in {skVar, skLet} and {sfGlobal, sfThread} * sym.flagsImpl == {} and
(sym.ownerFieldImpl == nil or sym.ownerFieldImpl.kindImpl != skModule))
(sym.kindImpl in {skVar, skLet} and {sfGlobal, sfThread} * sym.flagsImpl == {})
proc toNifSymName(w: var Writer; sym: PSym): string =
## Generate NIF name for a symbol: local names are `ident.disamb`,
@@ -524,7 +523,9 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
of nkEmpty:
if n.typField != nil:
w.withNode dest, n:
discard
let info = trLineInfo(w, n.info)
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), info
dest.addParRi
else:
let info = trLineInfo(w, n.info)
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), info
@@ -699,10 +700,7 @@ proc writeOp(w: var Writer; content: var TokenBuf; op: LogEntry) =
of MethodEntry:
discard "to implement"
of EnumToStrEntry:
content.addParLe repEnumToStrTag, NoLineInfo
content.add strToken(pool.strings.getOrIncl(op.key), NoLineInfo)
content.add symToken(pool.syms.getOrIncl(w.toNifSymName(op.sym)), NoLineInfo)
content.addParRi()
discard "to implement"
of GenericInstEntry:
discard "will only be written later to ensure it is materialized"
@@ -912,20 +910,20 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
proc createTypeStub(c: var DecodeContext; t: SymId): PType =
let name = pool.syms[t]
assert name.startsWith("`t")
var i = len("`t")
var k = 0
while i < name.len and name[i] in {'0'..'9'}:
k = k * 10 + name[i].ord - ord('0')
inc i
if i < name.len and name[i] == '.': inc i
var itemId = 0'i32
while i < name.len and name[i] in {'0'..'9'}:
itemId = itemId * 10'i32 + int32(name[i].ord - ord('0'))
inc i
if i < name.len and name[i] == '.': inc i
let suffix = name.substr(i)
result = c.types.getOrDefault(name)[0]
if result == nil:
var i = len("`t")
var k = 0
while i < name.len and name[i] in {'0'..'9'}:
k = k * 10 + name[i].ord - ord('0')
inc i
if i < name.len and name[i] == '.': inc i
var itemId = 0'i32
while i < name.len and name[i] in {'0'..'9'}:
itemId = itemId * 10'i32 + int32(name[i].ord - ord('0'))
inc i
if i < name.len and name[i] == '.': inc i
let suffix = name.substr(i)
let id = ItemId(module: moduleId(c, suffix).int32, item: itemId)
let offs = c.getOffset(id.module.FileIndex, name)
result = PType(itemId: id, uniqueId: id, kind: TTypeKind(k), state: Partial)
@@ -946,26 +944,30 @@ proc extractLocalSymsFromTree(c: var DecodeContext; n: var Cursor; thisModule: s
if n.tagId == sdefTag:
# Found an sdef - check if it's local
let name = n.firstSon
expect name, SymbolDef
let symName = pool.syms[name.symId]
let sn = parseSymName(symName)
if sn.module.len == 0 and symName notin localSyms:
# Local symbol - create stub and immediately load it fully
# since local symbols have no index offsets for lazy loading
let module = moduleId(c, thisModule)
let val = addr c.mods[module].symCounter
inc val[]
let id = ItemId(module: module.int32, item: val[])
let sym = PSym(itemId: id, kindImpl: skStub, name: c.cache.getIdent(sn.name),
disamb: sn.count.int32, state: Complete)
localSyms[symName] = sym
# Load the full symbol definition immediately
# We're currently at the `(sd` position, need to skip to SymbolDef
inc n # skip past `sd` tag to get to SymbolDef
loadSymFromCursor(c, sym, n, thisModule, localSyms)
sym.state = Sealed # mark as fully loaded
# Continue processing - loadSymFromCursor already advanced n past the closing `)`
continue
if name.kind == SymbolDef:
let symName = pool.syms[name.symId]
let sn = parseSymName(symName)
if sn.module.len == 0 and symName notin localSyms:
# Local symbol - create stub and immediately load it fully
# since local symbols have no index offsets for lazy loading
let module = moduleId(c, thisModule)
let val = addr c.mods[module].symCounter
inc val[]
let id = ItemId(module: module.int32, item: val[])
let sym = PSym(itemId: id, kindImpl: skStub, name: c.cache.getIdent(sn.name),
disamb: sn.count.int32, state: Complete)
localSyms[symName] = sym
# Load the full symbol definition immediately
# We're currently at the `(sd` position, need to skip to SymbolDef
inc n # skip past `sd` tag to get to SymbolDef
inc depth # account for the opening `(` of the sdef
loadSymFromCursor(c, sym, n, thisModule, localSyms)
sym.state = Sealed # mark as fully loaded
# loadSymFromCursor consumed everything including the closing `)`,
# so we need to account for it in depth tracking
dec depth
# Continue processing - loadSymFromCursor already advanced n past the closing `)`
continue
inc depth
elif n.kind == ParRi:
dec depth

View File

@@ -701,7 +701,6 @@ type
PLib* = ref TLib
TSym* {.acyclic.} = object # Keep in sync with ast2nif.nim
# Check `transitionSymKindCommon` in ast.nim when add a new field.
itemId*: ItemId
# proc and type instantiations are cached in the generic symbol
state*: ItemState

View File

@@ -230,29 +230,20 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.isDefined("nimsso"):
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
skipTypes(a.t, abstractVar + abstractInst).kind == tyString:
let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra
else: addrLoc(p.config, a)
result = (
cCast(ptrType(dest), cOp(Add, NimInt,
cCall(cgsymValue(p.module, "nimStrData"), strPtr), rb)),
lengthExpr)
var val: Snippet
if atyp.kind in {tyVar} and not compileToCpp(p.module):
val = cDeref(ra)
else:
var val: Snippet
if atyp.kind in {tyVar} and not compileToCpp(p.module):
val = cDeref(ra)
else:
val = ra
result = (
cIfExpr(dataFieldAccessor(p, val),
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
NimNil),
lengthExpr)
val = ra
result = (
cIfExpr(dataFieldAccessor(p, val),
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
NimNil),
lengthExpr)
else:
result = ("", "")
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -296,22 +287,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.isDefined("nimsso"):
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
skipTypes(n.typ, abstractVar + abstractInst).kind == tyString:
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), cDeref(ra)))
else:
result.add(cCall(cgsymValue(p.module, "nimStrData"), addrLoc(p.config, a)))
result.addArgumentSeparator()
result.add(lenExpr(p, a))
elif ntyp.kind in {tyVar} and not compileToCpp(p.module):
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t)
@@ -335,14 +315,9 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t)
if p.config.isDefined("nimsso"):
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet))
else:
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
result.addArgumentSeparator()
result.add(lt)
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
result.addArgumentSeparator()
result.add(lt)
of tyArray:
let ra = rdLoc(a)
result.add(ra)
@@ -369,8 +344,7 @@ proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
var a = initLocExpr(p, n[0])
let tmp = withTmpIfNeeded(p, a, needsTmp)
let ra = if p.config.isDefined("nimsso"): addrLoc(p.config, tmp) else: tmp.rdLoc
let ra = withTmpIfNeeded(p, a, needsTmp).rdLoc
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =

View File

@@ -216,8 +216,6 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
flags
let t = skipTypes(dest.t, abstractInst).getUniqueType()
for i, t in t.ikids:
# Do not produce code for void types
if isEmptyType(t): continue
let field = "Field$1" % [i.rope]
genAssignment(p, optAsgnLoc(dest, t, field),
optAsgnLoc(src, t, field), newflags)
@@ -320,16 +318,12 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
p.s(cpsStmts).addCallStmt(
cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
let rd = d.rdLoc
let ra = a.rdLoc
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
let la = lenExpr(p, a)
if p.config.isDefined("nimsso"):
let bra = byRefLoc(p, a)
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cCall(cgsymValue(p.module, "nimStrData"), bra))
else:
let ra = a.rdLoc
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
p.s(cpsStmts).addFieldAssignment(rd, "Field1", la)
else:
internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
@@ -926,8 +920,8 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
else:
a = initLocExprSingleUse(p, e[0])
# bug #23453 #25265
if e.typ != nil and e.typ.skipTypes(abstractInst).kind == tyObject:
if e.typ != nil and e.typ.kind == tyObject:
# bug #23453 #25265
discard getTypeDesc(p.module, e.typ)
if d.k == locNone:
# dest = *a; <-- We do not know that 'dest' is on the heap!
@@ -962,8 +956,7 @@ 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 and
not p.config.isDefined("nimsso"):
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions:
let strCandidate = n[0]
if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
var a: TLoc = initLocExpr(p, strCandidate)
@@ -989,9 +982,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# bug #19497
d.lode = e
else:
let ssoStrSub = p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString
var a: TLoc = initLocExpr(p, e[0], if ssoStrSub: {lfEnforceDeref, lfPrepareForMutation} else: {})
var a: TLoc = initLocExpr(p, e[0])
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
# addr (conv x) introduces a temp because `conv x` is not a rvalue
# transform addr ( conv ( x ) ) -> conv ( addr ( x ) )
@@ -1318,24 +1309,13 @@ 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 p.config.isDefined("nimsso") and ty.kind == tyString:
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
if lfPrepareForMutation in d.flags:
# Use nimStrAtMutV3 to get a mutable reference (char*) to the element.
# Only when mutation is requested: avoids calling nimPrepareStrMutationV2
# on const string literals (which would SIGSEGV on write to read-only memory).
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)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
let ra = rdLoc(a)
putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
@@ -1607,51 +1587,6 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
genAssignment(p, dest, b, {needToCopy})
gcUsage(p.config, e)
proc genSeqElemAppendV2(p: BProc, e: PNode, d: var TLoc) =
# s.add(x) with optSeqDestructors (arc/orc), inlined for direct slot construction:
# NI oldLen = s.len;
# if (s.p == NIM_NIL || (s.p->cap & ~NIM_STRLIT_FLAG) < oldLen + 1)
# s.p = (PayloadType*)prepareSeqAddUninit(oldLen, s.p, 1, sizeof(T), alignof(T));
# s.len = oldLen + 1;
# s.p->data[oldLen] = x; // direct assignment, no function call overhead
let seqtype = skipTypes(e[1].typ, abstractVarRange)
var a = initLocExpr(p, e[1])
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
# Capture a stable pointer to the seq BEFORE evaluating the element (e[2]).
# Evaluating e[2] may emit move semantics (eqwasMoved) that nil a variable
# through which e[1]'s snippet is accessed (e.g. a closure env pointer).
inc(p.labels)
let seqPtrName = "T" & rope(p.labels) & "_"
p.s(cpsLocals).addVar(kind = Local, name = seqPtrName,
typ = ptrType(getTypeDesc(p.module, seqtype)))
p.s(cpsStmts).addAssignment(seqPtrName, cAddr(rdLoc(a)))
var b = initLocExpr(p, e[2])
# All seq operations now go through the stable seqPtrName pointer.
let ra = wrapPar(cDeref(seqPtrName))
var tmpL = getIntTemp(p)
p.s(cpsStmts).addAssignment(tmpL.snippet, dotField(ra, "len"))
let pField = dotField(ra, "p")
p.s(cpsStmts).addSingleIfStmt(
cOp(Or,
cOp(Equal, pField, NimNil),
cOp(LessThan,
cOp(BitAnd, NimInt, derefField(pField, "cap"), cOp(BitNot, NimInt, NimStrlitFlag)),
cOp(Add, NimInt, tmpL.snippet, cIntValue(1))))):
p.s(cpsStmts).addFieldAssignmentWithValue(ra, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "prepareSeqAddUninit"),
tmpL.snippet,
pField,
cIntValue(1),
cSizeof(pe),
cAlignof(pe))
p.s(cpsStmts).addFieldAssignment(ra, "len",
cOp(Add, NimInt, tmpL.snippet, cIntValue(1)))
var dest = initLoc(locExpr, e[2], OnHeap)
dest.snippet = subscript(dataField(p, ra), tmpL.snippet)
genAssignment(p, dest, b, {})
proc genDefault(p: BProc; n: PNode; d: var TLoc) =
if d.k == locNone: d = getTemp(p, n.typ, needsInit=true)
else: resetLoc(p, d)
@@ -1787,15 +1722,15 @@ proc genNewSeq(p: BProc, e: PNode) =
let seqtype = skipTypes(e[1].typ, abstractVarRange)
let ra = a.rdLoc
let rb = b.rdLoc
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(ra, "len", rb)
p.s(cpsStmts).addFieldAssignmentWithValue(ra, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayload"),
rb,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0
genNewSeqAux(p, a, b.rdLoc, lenIsZero)
@@ -1808,15 +1743,15 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
if d.k == locNone: d = getTemp(p, e.typ, needsInit=false)
let rd = d.rdLoc
let ra = a.rdLoc
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(rd, "len", cIntValue(0))
p.s(cpsStmts).addFieldAssignmentWithValue(rd, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayloadUninit"),
ra,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) # bug #22560
let ra = a.rdLoc
@@ -1952,15 +1887,15 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
if optSeqDestructors in p.config.globalOptions:
let seqtype = n.typ
let rd = rdLoc dest[]
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(rd, "len", lit)
p.s(cpsStmts).addFieldAssignmentWithValue(rd, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayload"),
lit,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
# generate call to newSeq before adding the elements per hand:
genNewSeqAux(p, dest[], lit, n.len == 0)
@@ -1993,15 +1928,15 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
let seqtype = n.typ
let rd = rdLoc d
let valL = cIntValue(L)
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(rd, "len", valL)
p.s(cpsStmts).addFieldAssignmentWithValue(rd, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayload"),
valL,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
let lit = cIntLiteral(L)
genNewSeqAux(p, d, lit, L == 0)
@@ -2142,20 +2077,12 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
let ra = rdLoc(a)
putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage)
of tyString, tySequence:
let ra = rdLoc(a)
let la = lenExpr(p, a)
if p.config.isDefined("nimsso") and
skipTypes(a.t, abstractVarRange).kind == tyString:
let bra = byRefLoc(p, a)
putIntoDest(p, b, e,
cCall(cgsymValue(p.module, "nimStrData"), bra) &
cArgumentSeparator & la,
a.storage)
else:
let ra = rdLoc(a)
putIntoDest(p, b, e,
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil) &
cArgumentSeparator & la,
a.storage)
putIntoDest(p, b, e,
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil) &
cArgumentSeparator & la,
a.storage)
of tyArray:
let ra = rdLoc(a)
let la = cIntValue(lengthOrd(p.config, a.t))
@@ -2736,9 +2663,9 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc = initLocExpr(p, n[0])
let arg = if p.config.isDefined("nimsso"): addrLoc(p.config, a) else: rdLoc(a)
putIntoDest(p, d, n,
cgCall(p, "nimToCStringConv", arg),
cgCall(p, "nimToCStringConv", rdLoc(a)),
# "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
a.storage)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
@@ -2809,25 +2736,19 @@ proc genWasMoved(p: BProc; n: PNode) =
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
proc genMove(p: BProc; n: PNode; d: var TLoc) =
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref})
if n.len == 4:
# generated by liftdestructors:
var src: TLoc = initLocExpr(p, n[2])
let destVal = rdLoc(a)
let srcVal = rdLoc(src)
if p.config.isDefined("nimsso") and
n[1].typ.skipTypes(abstractVar).kind == tyString:
# SmallString: destroy dst then struct-copy src; no .p field aliasing needed
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(destVal, "p"),
dotField(srcVal, "p"))):
genStmts(p, n[3])
genAssignment(p, a, src, {})
else:
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(destVal, "p"),
dotField(srcVal, "p"))):
genStmts(p, n[3])
p.s(cpsStmts).addFieldAssignment(destVal, "len", dotField(srcVal, "len"))
p.s(cpsStmts).addFieldAssignment(destVal, "p", dotField(srcVal, "p"))
p.s(cpsStmts).addFieldAssignment(destVal, "len", dotField(srcVal, "len"))
p.s(cpsStmts).addFieldAssignment(destVal, "p", dotField(srcVal, "p"))
else:
if d.k == locNone: d = getTemp(p, n.typ)
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}:
@@ -2864,19 +2785,15 @@ proc genDestroy(p: BProc; n: PNode) =
case t.kind
of tyString:
var a: TLoc = initLocExpr(p, arg)
if p.config.isDefined("nimsso"):
# SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a))
else:
let ra = rdLoc(a)
let rp = dotField(ra, "p")
p.s(cpsStmts).addSingleIfStmt(
cOp(And, rp,
cOp(Not, cOp(BitAnd, NimInt,
derefField(rp, "cap"),
NimStrlitFlag)))):
let fn = if optThreads in p.config.globalOptions: "deallocShared" else: "dealloc"
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, fn), rp)
let ra = rdLoc(a)
let rp = dotField(ra, "p")
p.s(cpsStmts).addSingleIfStmt(
cOp(And, rp,
cOp(Not, cOp(BitAnd, NimInt,
derefField(rp, "cap"),
NimStrlitFlag)))):
let fn = if optThreads in p.config.globalOptions: "deallocShared" else: "dealloc"
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, fn), rp)
of tySequence:
var a: TLoc = initLocExpr(p, arg)
let ra = rdLoc(a)
@@ -2981,15 +2898,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem:
if optSeqDestructors in p.config.globalOptions:
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
# Inline growth + direct slot assignment: avoids the add() call overhead
# and lets the C compiler see the construction expression at its final
# destination, enabling in-place construction for nkObjConstr etc.
# gcYrc is excluded because its add() acquires a striped reader lock.
genSeqElemAppendV2(p, e, d)
else:
e[1] = makeAddr(e[1], p.module.idgen)
genCall(p, e, d)
e[1] = makeAddr(e[1], p.module.idgen)
genCall(p, e, d)
else:
genSeqElemAppend(p, e, d)
of mEqStr: genStrEquals(p, e, d)
@@ -3245,8 +3155,6 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
for i in 0..<n.len:
var it = n[i]
if it.kind == nkExprColonExpr: it = it[1]
# Do not produce code for void types
if it.typ != nil and isEmptyType(it.typ): continue
rec = initLoc(locExpr, it, dest[].storage)
rec.snippet = dotField(rdLoc(dest[]), "Field" & rope(i))
rec.flags.incl(lfEnforceDeref)
@@ -3858,7 +3766,6 @@ proc containsOpaqueImportcField(typ: PType): bool =
return true
of tyTuple:
for i, a in t.ikids:
if isEmptyType(a): continue
if containsOpaqueImportcField(a):
return true
of tyArray:
@@ -3908,12 +3815,10 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder)
var tupleInit: StructInitializer
let initKind = if containsOpaqueImportcField(t): siNamedStruct else: siOrderedStruct
result.addStructInitializer(tupleInit, kind = initKind):
if p.vccAndC and validTupleTypeFields(t) == 0:
if p.vccAndC and t.isEmptyTupleType:
result.addField(tupleInit, name = "dummy"):
result.addIntValue(0)
for i, a in t.ikids:
# Do not produce code for void types
if isEmptyType(a): continue
let elemTyp = skipTypes(a, abstractRange+{tyOwned}-{tyTypeDesc})
if not isOpaqueImportcType(elemTyp):
result.addField(tupleInit, name = "Field" & $i):
@@ -4088,8 +3993,6 @@ proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Bu
var it = n[i]
if it.kind == nkExprColonExpr:
it = it[1]
# Do not produce code for void types
if isEmptyType(tup[i]): continue
result.addField(tupleInit, name = "Field" & $i):
genBracedInit(p, it, isConst, tup[i], result)
@@ -4236,10 +4139,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
genConstObjConstr(p, n, isConst, result)
of tyString, tyCstring:
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
if p.config.isDefined("nimsso"):
genStringLiteralV3Const(p.module, n, isConst, result)
else:
genStringLiteralV2Const(p.module, n, isConst, result)
genStringLiteralV2Const(p.module, n, isConst, result)
else:
var d: TLoc = initLocExpr(p, n)
result.add rdLoc(d)

View File

@@ -22,11 +22,7 @@ template detectVersion(field, corename) =
result = 1
proc detectStrVersion(m: BModule): int =
if m.g.config.isDefined("nimsso") and
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
result = 3
else:
detectVersion(strVersion, "nimStrVersion")
detectVersion(strVersion, "nimStrVersion")
proc detectSeqVersion(m: BModule): int =
detectVersion(seqVersion, "nimSeqVersion")
@@ -132,192 +128,6 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Bu
result.addField(strInit, name = "p"):
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
proc ssoCharLit(ch: char): string =
## Return a C char literal for ch, with proper escaping.
const hexDigits = "0123456789abcdef"
result = "'"
case ch
of '\'': result.add("\\'")
of '\\': result.add("\\\\")
of '\0': result.add("\\0")
of '\n': result.add("\\n")
of '\r': result.add("\\r")
of '\t': result.add("\\t")
elif ch.ord < 32 or ch.ord == 127:
result.add("\\x")
result.add(hexDigits[ch.ord shr 4])
result.add(hexDigits[ch.ord and 0xf])
else:
result.add(ch)
result.add('\'')
proc ssoBytesLit(m: BModule; s: string; slen: int): string =
## Compute the `bytes` field value for the new SmallString layout.
## byte 0 = slen, bytes 1-7 = inline chars 0-6 (zero-padded).
## On LE: slen in bits 0-7, char[i] in bits (i+1)*8..(i+1)*8+7.
## On BE: slen in bits 56-63, char[i] in bits (6-i)*8..(6-i)*8+7.
const AlwaysAvail = 7
var val: uint64
if CPU[m.g.config.target.targetCPU].endian == littleEndian:
val = uint64(slen)
for i in 0..<min(s.len, AlwaysAvail):
val = val or (uint64(s[i]) shl (uint(i + 1) * 8))
else:
val = uint64(slen) shl 56
for i in 0..<min(s.len, AlwaysAvail):
val = val or (uint64(s[i]) shl (uint(AlwaysAvail - 1 - i) * 8))
# Cast to NU (C name for Nim's uint, = NU64 on 64-bit). NU64 = uint64_t.
result = cCast("NU", $val & "ULL")
proc ssoMoreLit(m: BModule; s: string): string =
## For medium string literals (AlwaysAvail < len <= PayloadSize), encode
## chars[AlwaysAvail..ptrSize-1] in the 'more' pointer field bit-pattern.
## The last pointer byte is always '\0' (null terminator), guaranteed by
## PayloadSize = AlwaysAvail + ptrSize - 1. slen <= PayloadSize guards
## prevent any code from dereferencing this as an actual pointer.
const AlwaysAvail = 7
let ptrSize = m.g.config.target.ptrSize
var val: uint64 = 0
for i in 0..<ptrSize:
let ch: uint64 = if AlwaysAvail + i < s.len: uint64(s[AlwaysAvail + i]) else: 0
if CPU[m.g.config.target.targetCPU].endian == littleEndian:
val = val or (ch shl (uint(i) * 8))
else:
val = val or (ch shl (uint(ptrSize - 1 - i) * 8))
result = cCast(ptrType("LongString"), "(uintptr_t)" & $val)
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
# Inline SmallString struct initializer for use inside const aggregate types.
# Layout: {bytes: NimUint, more: ptr LongString}
# bytes = slen (low byte) | char[0]<<8 | char[1]<<16 | ... | char[6]<<56
const AlwaysAvail = 7
let s = n.strVal
cgsym(m, "SmallString")
cgsym(m, "LongString")
let payloadSize = AlwaysAvail + m.g.config.target.ptrSize - 1
var si: StructInitializer
result.addStructInitializer(si, kind = siOrderedStruct):
if s.len <= AlwaysAvail:
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, s.len))
result.addField(si, name = "more"):
result.add(NimNil)
elif s.len <= payloadSize:
# Medium string: bytes holds slen + chars 0-6; more holds chars 7..PayloadSize-1.
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, s.len))
result.addField(si, name = "more"):
result.add(ssoMoreLit(m, s))
else:
# Emit the LongString block into cfsStrData and reference it inline.
let dataName = getTempName(m)
var res = newBuilder("")
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = dataName):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "rc", typ = NimInt)
res.addField(name = "fullLen", typ = NimInt)
res.addField(name = "capImpl", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var di: StructInitializer
res.addStructInitializer(di, kind = siOrderedStruct):
res.addField(di, name = "fullLen"):
res.addIntValue(s.len)
res.addField(di, name = "rc"):
res.addIntValue(1)
res.addField(di, name = "capImpl"):
res.addIntValue(0) # static, never freed
res.addField(di, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
# slen = StaticSlen (254): marks this as a static (never-freed) long string.
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, 254))
result.addField(si, name = "more"):
result.add(cCast(ptrType("LongString"), cAddr(dataName)))
# ------ Version 3: SmallString (SSO) strings --------------------------------
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder) =
# SmallString literal. Always generate a fresh SmallString variable (like v2
# always generates a fresh outer NimStringV2). For long strings, cache the
# LongString payload to avoid duplicates within a module.
const AlwaysAvail = 7 # must match strs_v3.nim
let s = n.strVal
let tmp = getTempName(m)
result.add tmp
cgsym(m, "SmallString")
cgsym(m, "LongString")
let payloadSize = AlwaysAvail + m.g.config.target.ptrSize - 1
var res = newBuilder("")
if s.len <= AlwaysAvail:
# Short: bytes holds slen + all chars (zero-padded), more = NULL.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, s.len))
res.addField(si, name = "more"):
res.add(NimNil)
elif s.len <= payloadSize:
# Medium: bytes holds slen + chars 0-6; more holds chars 7..PayloadSize-1 as raw bits.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, s.len))
res.addField(si, name = "more"):
res.add(ssoMoreLit(m, s))
else:
# Long: cache the LongString block to emit it only once per module per string.
# Always generate a fresh SmallString pointing at the (possibly cached) block.
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var dataName: string
if id == m.labels:
dataName = getTempName(m)
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = dataName):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "rc", typ = NimInt)
res.addField(name = "fullLen", typ = NimInt)
res.addField(name = "capImpl", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var di: StructInitializer
res.addStructInitializer(di, kind = siOrderedStruct):
res.addField(di, name = "fullLen"):
res.addIntValue(s.len)
res.addField(di, name = "rc"):
res.addIntValue(1)
res.addField(di, name = "capImpl"):
res.addIntValue(0) # bit 0 = 0: static, never freed
res.addField(di, name = "data"):
res.add(makeCString(s))
else:
dataName = m.tmpBase & $id
# slen = StaticSlen (254): marks this as a static (never-freed) long string.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, 254))
res.addField(si, name = "more"):
res.add(cCast(ptrType("LongString"), cAddr(dataName)))
m.s[cfsStrData].add(extract(res))
# ------ Version selector ---------------------------------------------------
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
@@ -328,8 +138,6 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
let tmp = getTempName(m)
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
result.add tmp
of 3:
localError(m.config, info, "genStringLiteralDataOnly not supported for SmallString (nimsso)")
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
@@ -340,6 +148,5 @@ proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
case detectStrVersion(m)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)
of 3: genStringLiteralV3(m, n, isConst = true, result)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View File

@@ -1940,15 +1940,6 @@ 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

@@ -309,7 +309,7 @@ proc addAbiCheck(m: BModule; t: PType, name: Rope) =
proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
backendEnsureMutable param.sym
ensureMutable param.sym
fillLoc(param.sym.locImpl, locParam, param, "Result",
OnStack)
let t = param.sym.typ
@@ -339,10 +339,6 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
result = typeNameOrLiteral(m, typ, "NimStringV2")
of 3:
cgsym(m, "LongString")
cgsym(m, "SmallString")
result = typeNameOrLiteral(m, typ, "SmallString")
else:
cgsym(m, "NimStringDesc")
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
@@ -457,14 +453,6 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope =
result = getTypeDescWeak(m, t, check, dkParam) & "_Content"
#result = getTypeForward(m, t, hashType(t)) & "_Content"
proc seqPayloadElem(m: BModule; t: PType): Snippet =
## Returns the C type name for a seq's element as stored in the payload,
## suitable for sizeof()/alignof(). Must use dkVar, not the dkParam default,
## because reified openArrays (experimental views) differ: dkParam gives a
## bare pointer (T*) while dkVar gives the two-word struct actually stored.
var check = initIntSet()
result = getTypeDescAux(m, t.elementType, check, dkVar)
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
let sig = hashType(t, m.config)
let result = cacheGetType(m.typeCache, sig)
@@ -546,7 +534,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
var types, names, args: seq[string] = @[]
if not isCtor:
var this = t.n[1].sym
backendEnsureMutable this
ensureMutable this
fillParamName(m, this)
fillLoc(this.locImpl, locParam, t.n[1],
this.paramStorageLoc)
@@ -568,7 +556,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
else:
descKind = dkRefParam
var typ, name: string
backendEnsureMutable param
ensureMutable param
fillParamName(m, param)
fillLoc(param.locImpl, locParam, t.n[i],
param.paramStorageLoc)
@@ -812,8 +800,6 @@ proc getTupleDesc(m: BModule; typ: PType, name: Rope,
var res = newBuilder("")
res.addStruct(m, typ, name, ""):
for i, a in typ.ikids:
# Do not produce code for void types
if isEmptyType(a): continue
res.addField(
name = "Field" & $i,
typ = getTypeDescAux(m, a, check, dkField))
@@ -1187,7 +1173,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Builder; asPtr: bool
let isCtor = sfConstructor in prc.flags
var check = initIntSet()
fillBackendName(m, prc)
backendEnsureMutable prc
ensureMutable prc
fillLoc(prc.locImpl, locProc, prc.ast[namePos], OnUnknown)
var memberOp = "#." #only virtual
var typ: PType
@@ -1486,38 +1472,27 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo
t.incl tfObjHasKids
t = t.baseClass
proc validTupleTypeFields(t: PType): int =
# we want to treat tuples with only void fields as empty, so we need to exclude void types here:
result = 0
for a in t.kids:
if not isEmptyType(a): inc result
proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
genTypeInfoAuxBase(m, typ, typ, name, cIntValue(0), info)
var expr = getNimNode(m)
let nonVoidKids = validTupleTypeFields(typ)
if nonVoidKids > 0:
var tmp = getTempName(m) & "_" & $nonVoidKids
genTNimNodeArray(m, tmp, nonVoidKids)
var j = 0
if not typ.isEmptyTupleType:
var tmp = getTempName(m) & "_" & $typ.kidsLen
genTNimNodeArray(m, tmp, typ.kidsLen)
for i, a in typ.ikids:
# Do not produce code for void types
if isEmptyType(a): continue
var tmp2 = getNimNode(m)
let fieldTypInfo = genTypeInfoV1(m, a, info)
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(j), cAddr(tmp2))
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(i), cAddr(tmp2))
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "kind", 1)
m.s[cfsTypeInit3].addFieldAssignmentWithValue(tmp2, "offset"):
m.s[cfsTypeInit3].addOffsetof(getTypeDesc(m, origType, dkVar), "Field" & $i)
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "typ", fieldTypInfo)
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "name", "\"Field" & $i & "\"")
inc j
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", nonVoidKids)
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", typ.kidsLen)
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
m.s[cfsTypeInit3].addFieldAssignment(expr, "sons",
cAddr(subscript(tmp, cIntValue(0))))
else:
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", cIntValue(0))
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", typ.kidsLen)
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "node", cAddr(expr))

View File

@@ -389,11 +389,7 @@ proc lenField(p: BProc, val: Rope): Rope {.inline.} =
proc lenExpr(p: BProc; a: TLoc): Rope =
if optSeqDestructors in p.config.globalOptions:
if p.config.isDefined("nimsso") and a.lode != nil and a.t != nil and
a.t.skipTypes(abstractInst).kind == tyString:
result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a))
else:
result = dotField(rdLoc(a), "len")
result = dotField(rdLoc(a), "len")
else:
let ra = rdLoc(a)
result = cIfExpr(ra, lenField(p, ra), cIntValue(0))
@@ -534,15 +530,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
let atyp = skipTypes(loc.t, abstractInst)
let rl = rdLoc(loc)
if typ.kind == tyString and p.config.isDefined("nimsso"):
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
if atyp.kind in {tyVar, tyLent}:
p.s(cpsStmts).addAssignment(derefField(rl, "bytes"), cIntValue(0))
p.s(cpsStmts).addAssignment(derefField(rl, "more"), NimNil)
else:
p.s(cpsStmts).addAssignment(dotField(rl, "bytes"), cIntValue(0))
p.s(cpsStmts).addAssignment(dotField(rl, "more"), NimNil)
elif atyp.kind in {tyVar, tyLent}:
if atyp.kind in {tyVar, tyLent}:
p.s(cpsStmts).addAssignment(derefField(rl, "len"), cIntValue(0))
p.s(cpsStmts).addAssignment(derefField(rl, "p"), NimNil)
else:
@@ -592,13 +580,8 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
let typ = loc.t
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
let rl = rdLoc(loc)
if skipTypes(typ, abstractInst + {tyStatic}).kind == tyString and p.config.isDefined("nimsso"):
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
p.s(cpsStmts).addFieldAssignment(rl, "bytes", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil)
else:
p.s(cpsStmts).addFieldAssignment(rl, "len", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "p", NimNil)
p.s(cpsStmts).addFieldAssignment(rl, "len", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "p", NimNil)
elif not isComplexValueType(typ):
if containsGarbageCollectedRef(loc.t):
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)

View File

@@ -139,7 +139,7 @@
import
ast, msgs, idents,
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos, trees
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
import std/tables
@@ -1390,34 +1390,18 @@ proc optimizeStates(ctx: var Ctx) =
for i in 0 .. ctx.states.high:
ctx.states[i].label.intVal = i
proc detectCapturedSym(c: var Ctx, s: PSym, stateIdx: int) =
if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym:
let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
if vs == localNotSeen: # First seing this variable
c.varStates[s.itemId] = stateIdx
elif vs == localRequiresLifting:
discard # Sym already marked
elif vs != stateIdx:
c.captureVar(s)
proc isClosureIterLocal(c: Ctx, s: PSym): bool =
s.kind in {skResult, skVar, skLet, skForVar, skTemp} and
sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym
proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
case n.kind
of nkSym:
let s = n.sym
detectCapturedSym(c, s, stateIdx)
of nkAddr, nkHiddenAddr:
let s = getRoot(n)
if s != nil and isClosureIterLocal(c, s):
detectCapturedSym(c, s, stateIdx)
# bug #25596; lifetime extension for `addr`-taken locals as
# we claim ARC/ORC do destruction based on scopes, not on last-usages.
c.captureVar(s)
for i in 0 ..< n.safeLen:
detectCapturedVars(c, n[i], stateIdx)
if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym:
let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
if vs == localNotSeen: # First seing this variable
c.varStates[s.itemId] = stateIdx
elif vs == localRequiresLifting:
discard # Sym already marked
elif vs != stateIdx:
c.captureVar(s)
of nkReturnStmt:
if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
# we have a `result = result` expression produced by the closure

View File

@@ -951,7 +951,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectArg(conf, switch, arg, pass, info)
var value: int = 10_000_000
discard parseSaturatedNatural(arg, value)
if value <= 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
conf.maxLoopIterationsVM = value
of "maxcalldepthvm":
expectArg(conf, switch, arg, pass, info)

View File

@@ -148,7 +148,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
limitB = iB
while limitA < aLen and isDigit(a[limitA]): inc limitA
while limitB < bLen and isDigit(b[limitB]): inc limitB
var pos = max(limitA-iA, limitB-iB)
var pos = max(limitA-iA, limitB-iA)
while pos > 0:
if limitA-pos < iA: # digit in `a` is 0 effectively
result = ord('0') - ord(b[limitB-pos])
@@ -540,11 +540,10 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
elif s != nil and s.kind in {skType, skVar, skLet, skConst} and
sfExported in s.flags and s.owner != nil and
belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml:
let href = (if d.module == s.owner: ""
else: externalDep(d, s.owner).changeFileExt("html")
) & "#" & literal
result.addf "<a href=\"$1\"><span class=\"Identifier\">$2</span></a>",
[href, escLit]
let external = externalDep(d, s.owner)
result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
[changeFileExt(external, "html"), literal,
escLit]
else:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [escLit])

View File

@@ -72,11 +72,9 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
assert(not containsGarbageCollectedRef(t))
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo; needsInit: bool): PNode =
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
sym.typ = typ
if not needsInit:
sym.incl sfNoInit
s.vars.add(sym)
result = newSymNode(sym)
@@ -304,7 +302,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
if deepAliases(dest, ri):
# consider: x = x + y, it is wrong to destroy the destination first!
# tmp to support self assignments
let tmp = c.getTemp(s, dest.typ, dest.info, needsInit = false)
let tmp = c.getTemp(s, dest.typ, dest.info)
result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
c.genDestroy(tmp))
else:
@@ -373,7 +371,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
# but fields within active case branch might need destruction
# tmp to support self assignments
let tmp = c.getTemp(s, n[1].typ, n.info, needsInit = false)
let tmp = c.getTemp(s, n[1].typ, n.info)
result = newTree(nkStmtList)
result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
@@ -460,52 +458,51 @@ proc isCapturedVar(n: PNode): bool =
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
if not hasDestructorOrAsgn(c, nTyp):
# Non-managed (plain-old-data) type: no ownership transfer is needed.
# Return the expression directly — no temp required.
if hasDestructorOrAsgn(c, nTyp):
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = c.getTemp(s, nTyp, n.info)
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, {}, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
# Since we know somebody will take over the produced copy, there is
# no need to destroy it.
result.add tmp
else:
if c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
assert(not containsManagedMemory(nTyp))
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
return p(n, c, s, normal)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = c.getTemp(s, nTyp, n.info, needsInit = false)
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, {}, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
# Since we know somebody will take over the produced copy, there is
# no need to destroy it.
result.add tmp
result = p(n, c, s, normal)
proc isDangerousSeq(t: PType): bool {.inline.} =
let t = t.skipTypes(abstractInst)
@@ -533,7 +530,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
# produce temp creation for (fn, env). But we need to move 'env'?
# This was already done in the sink parameter handling logic.
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
let tmp = c.getTemp(s, arg.typ, arg.info, true)
let tmp = c.getTemp(s, arg.typ, arg.info)
result.add c.genSink(s, tmp, arg, {IsDecl})
result.add tmp
s.final.add c.genDestroy(tmp)
@@ -612,7 +609,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
# later and use it to eliminate the temporary when theres no need for it, but its
# tricky because you would have to intercept moveOrCopy at a certain point
let tmp = c.getTemp(s.parent[], ret.typ, ret.info, needsInit = true)
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
tmp.sym.flags = tmpFlags
let cpy = if hasDestructor(c, ret.typ) and
ret.typ.kind notin {tyOpenArray, tyVarargs}:
@@ -773,7 +770,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result = copyNode(n)
result.add call
else:
let tmp = c.getTemp(s, n[0].typ, n.info, needsInit = true)
let tmp = c.getTemp(s, n[0].typ, n.info)
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
m.add p(n[0], c, s, normal)
c.finishCopy(m, n[0], {}, isFromSink = false)
@@ -1004,13 +1001,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
elif isDiscriminantField(n[0]):
result = c.genDiscriminantAsgn(s, n)
elif n[1].kind in {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock}:
# Distribute the assignment into each branch to avoid
# creating pointless temporaries for expression-based control flow.
let dest = p(n[0], c, s, mode)
template process(child, s): untyped =
newTree(n.kind, dest, p(child, c, s, consumed))
handleNestedTempl(n[1], process, willProduceStmt = true)
else:
result = copyNode(n)
result.add p(n[0], c, s, mode)
@@ -1164,7 +1154,7 @@ proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag])
break
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
let tmp = c.getTemp(s, n.typ, n.info, needsInit = true)
let tmp = c.getTemp(s, n.typ, n.info)
tmp.sym.flagsImpl.incl sfSingleUsedTemp
result.add newTree(nkFastAsgn, tmp, copyTree(n))
s.final.add c.genDestroy(tmp)

View File

@@ -460,9 +460,7 @@ proc addInt128*(result: var string; value: Int128) =
var i = initialSize
var j = high(result)
while i < j:
let tmp = result[i]
result[i] = result[j]
result[j] = tmp
swap(result[i], result[j])
i += 1
j -= 1

View File

@@ -2018,12 +2018,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
if indirect: result = "[$1]" % [result]
of tyTuple:
result = rope("{")
var first = true
for i in 0..<t.len:
# Do not produce code for void types
if isEmptyType(t[i]): continue
if not first: result.add(", ")
first = false
if i > 0: result.add(", ")
result.addf("Field$1: $2", [i.rope,
createVar(p, t[i], false)])
result.add("}")

View File

@@ -408,12 +408,6 @@ Consider:
proc isTypeOf(n: PNode): bool =
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
proc isEnvTypeForRoutine(envTyp: PType; routine: PSym): bool =
## True if `envTyp` is (maybe wrapped) env object type owned by `routine`, as
## created by `getEnvTypeForOwner` / `createEnvObj`.
let obj = envTyp.skipTypes({tyOwned, tyRef, tyPtr})
result = obj.kind == tyObject and obj.owner.id == routine.id
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
var cp = getEnvParam(fn)
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
@@ -424,13 +418,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
# Nested `liftLambdas` uses a fresh `DetectionPass`, so `getEnvTypeForOwner`
# can allocate another PType for the same logical env; the hidden param from
# the inner pass is authoritative (bug #21242).
if isEnvTypeForRoutine(cp.typ, owner) and isEnvTypeForRoutine(t, owner):
c.ownerToType[owner.id] = cp.typ
else:
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =

View File

@@ -46,11 +46,12 @@ proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
when useRef:
pt = pt.nextLayer
else:
# Must read nextLayer into a temp before destroying pt:
# `pt = pt.nextLayer[]` would call eqcopy(&pt, &(*pt.nextLayer)) which
# decrements pt.nextLayer's rc (freeing it) before reading pt.nextLayer.nextLayer.
let tmp = pt.nextLayer[]
pt = tmp
when defined(gcDestructors):
pt = pt.nextLayer[]
else:
# workaround refc
let tmp = pt.nextLayer[]
pt = tmp
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
var tm = pt

View File

@@ -620,34 +620,11 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
cond.typ = getSysType(c.g, c.info, tyBool)
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
proc genBulkCopySeq(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Generates a call to nimCopySeqPayload for bulk memcpy of seq data.
let elemType = t.elementType
let sym = magicsys.getCompilerProc(c.g, "nimCopySeqPayload")
if sym == nil:
localError(c.g.config, c.info, "system module needs: nimCopySeqPayload")
return
var sizeOf = genBuiltin(c, mSizeOf, "sizeof", newNodeIT(nkType, c.info, elemType))
sizeOf.typ = getSysType(c.g, c.info, tyInt)
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
let call = newNodeI(nkCall, c.info)
call.add newSymNode(sym)
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x)
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
call.add sizeOf
call.add alignOf
call.typ = sym.typ.returnType
body.add call
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
of attachedDup:
body.add setLenSeqCall(c, t, x, y)
if supportsCopyMem(t.elementType):
genBulkCopySeq(c, t, body, x, y)
else:
forallElements(c, t, body, x, y)
forallElements(c, t, body, x, y)
of attachedAsgn, attachedDeepCopy:
# we generate:
# if x.p == y.p:
@@ -656,13 +633,9 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# var i = 0
# while i < y.len: dest[i] = y[i]; inc(i)
# This is usually more efficient than a destroy/create pair.
# For trivially copyable types, use bulk copyMem instead of element loop.
checkSelfAssignment(c, t, body, x, y)
body.add setLenSeqCall(c, t, x, y)
if supportsCopyMem(t.elementType):
genBulkCopySeq(c, t, body, x, y)
else:
forallElements(c, t, body, x, y)
forallElements(c, t, body, x, y)
of attachedSink:
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
@@ -728,18 +701,11 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedAsgn, attachedDeepCopy, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
of attachedSink:
if c.g.config.isDefined("nimsso"):
# SmallString: destroy old dst, then bit-copy src (no rc increment — this is a move).
# No .p aliasing check needed; rc-based destroy handles COW sharing correctly.
doAssert t.destructor != nil
body.add destructorCall(c, t.destructor, x)
body.add newAsgnStmt(x, y)
else:
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
of attachedDestructor:
body.add genBuiltin(c, mDestroy, "destroy", x)
of attachedTrace:

View File

@@ -266,7 +266,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result = default(array[0..3, TNoteKinds])
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix, warnSystemRangeConversion}
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[1] = result[2] - {warnImplicitRangeConversion, warnProveField, warnProveIndex,
result[1] = result[2] - {warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,

View File

@@ -163,7 +163,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
inc(s.lineOffset)
result = min(bufLen, s.s.len - s.rd)
if result > 0:
copyMem(buf, readRawData(s.s, s.rd), result)
copyMem(buf, addr(s.s[s.rd]), result)
inc(s.rd, result)
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
@@ -173,7 +173,7 @@ proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
of llsString:
result = min(bufLen, s.s.len - s.rd)
if result > 0:
copyMem(buf, readRawData(s.s, s.rd), result)
copyMem(buf, addr(s.s[0 + s.rd]), result)
inc(s.rd, result)
of llsFile:
result = readBuffer(s.f, buf, bufLen)

View File

@@ -11,7 +11,7 @@
import
ast, msgs, platform, idents,
modulegraphs, lineinfos, types
modulegraphs, lineinfos
export createMagic
@@ -134,7 +134,7 @@ proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()
proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
case t.skipTypes(abstractRange).kind
case t.kind
of tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
result = getSysMagic(g, info, "==", mEqI)

View File

@@ -66,7 +66,6 @@ type
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
enumToStringProcs*: Table[ItemId, PSym]
loadedEnumToStringProcs: Table[string, PSym]
emittedTypeInfo*: Table[string, FileIndex]
packageSyms*: TStrTable
@@ -148,7 +147,6 @@ proc resetForBackend*(g: ModuleGraph) =
a.clear()
g.methodsPerGenericType.clear()
g.enumToStringProcs.clear()
g.loadedEnumToStringProcs.clear()
g.dispatchers.setLen(0)
g.methodsPerType.clear()
for a in mitems(g.loadedOps):
@@ -334,10 +332,7 @@ iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
yield it
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
result = g.enumToStringProcs.getOrDefault(t.itemId)
if result == nil and g.config.cmd in {cmdNifC, cmdM}:
let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
result = g.loadedEnumToStringProcs.getOrDefault(key)
result = g.enumToStringProcs[t.itemId]
assert result != nil
proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
@@ -697,7 +692,7 @@ when not defined(nimKochBootstrap):
of MethodEntry:
discard "todo"
of EnumToStrEntry:
g.loadedEnumToStringProcs[x.key] = x.sym
discard "todo"
of GenericInstEntry:
raiseAssert "GenericInstEntry should not be in the NIF index"
# Register methods per type from NIF index

View File

@@ -44,16 +44,14 @@ proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[Precomp
let suffix = stack.pop()
if not visited.containsOrIncl(suffix.string):
var isKnownFile = false
let fileIdx = g.config.registerNifSuffix(suffix.string, isKnownFile)
let nifFile = toGeneratedFile(g.config, AbsoluteFile(suffix.string), ".nif")
let fileIdx = msgs.fileInfoIdx(g.config, nifFile)
let precomp = moduleFromNifFile(g, fileIdx, {LoadFullAst})
if precomp.module != nil:
result.add precomp
for dep in precomp.deps:
if not visited.contains(dep.string):
stack.add dep
else:
assert false, "Recompiling module is not implemented."
if mainModule.module != nil:
result.add mainModule

View File

@@ -160,13 +160,9 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
if z.state == csMatch:
# Iterator preference is heuristic in iterator-admitting contexts.
# The dedicated iterable path uses `iteratorPreference`, other
# context use exact-match bump
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator:
if efPreferIteratorForIterable in flags:
inc(z.iteratorPreference)
elif not (efWantIterator notin flags and efWantIterable in flags):
if not (efWantIterator notin flags and efWantIterable in flags):
inc(z.exactMatches, 200)
else:
dec(z.exactMatches, 200)
@@ -675,7 +671,7 @@ proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
# copied from semOverloadedCallAnalyzeEffects, might be overkill:
const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
let filter =
if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}:
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
baseFilter + {skIterator}
else: baseFilter
# this will add the errors:

View File

@@ -54,18 +54,7 @@ type
inst*: PInstantiation
TExprFlag* = enum
efLValue,
# The expression is used as an assignable location.
efWantIterator,
# Admit iterator candidates and prefer them during overload resolution.
efWantIterable,
# Admit iterator candidates for expressions that may feed iterable-style
# chaining.
efPreferIteratorForIterable,
# Prefer iterator candidates for `iterable[T]` matching and wrap a
# successful iterator call as `tyIterable`.
efInTypeof,
# The expression is being semchecked under `typeof`.
efLValue, efWantIterator, efWantIterable, efInTypeof,
efNeedStatic,
# Use this in contexts where a static value is mandatory
efPreferStatic,

View File

@@ -979,7 +979,7 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags; expectedType: PType = nil): PNode =
if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}:
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
# for typeof support.
@@ -1006,8 +1006,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
# See bug #2051:
result[0] = newSymNode(errorSym(c, n))
elif callee.kind == skIterator:
if result.typ.kind != tyIterable and
flags * {efWantIterable, efPreferIteratorForIterable} != {}:
if efWantIterable in flags:
let typ = newTypeS(tyIterable, c)
rawAddSon(typ, result.typ)
result.typ = typ
@@ -1526,7 +1525,7 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
return
# extra flags since LHS may become a call operand:
n[0] = semExprWithType(c, n[0], flags + {efDetermineType, efWantIterable, efAllowSymChoice})
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
#restoreOldStyleType(n[0])
var i = considerQuotedIdent(c, n[1], n)
var ty = n[0].typ

View File

@@ -232,7 +232,10 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
of "stripGenericParams":
result = uninstantiate(operand).toNode(traitCall.info)
of "supportsCopyMem":
result = newIntNodeT(toInt128(ord(supportsCopyMem(operand))), traitCall, c.idgen, c.graph)
let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t)
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
of "canFormCycles":
result = newIntNodeT(toInt128(ord(types.canFormAcycle(c.graph, operand))), traitCall, c.idgen, c.graph)
of "hasDefaultValue":

View File

@@ -1096,12 +1096,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
let iterBase = n[^2].typ
let iterType =
if iterBase.kind == tyIterable:
iterBase.skipModifier
else:
skipTypes(iterBase, {tyAlias, tySink, tyOwned})
var iter = skipTypes(iterType, {tyGenericInst})
var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned})
var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar})
# n.len == 3 means that there is one for loop variable
# and thus no tuple unpacking:
@@ -1134,9 +1129,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
else:
var v = symForVar(c, n[0])
if getCurrOwner(c).kind == skModule: incl(v, sfGlobal)
# Use `iterType` here: it removes outer `tyIterable` / alias-like wrappers
# from the loop source, but still preserves `tyGenericInst` for the loop var.
v.typ = iterType
# BUGFIX: don't use `iter` here as that would strip away
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
# for an example:
v.typ = iterBase
n[0] = newSymNode(v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
@@ -1200,14 +1196,14 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
c.p.breakInLoop = oldBreakInLoop
dec(c.p.nestedLoopCounter)
proc implicitIterator(c: PContext, it: string, arg: PNode, flags: TExprFlags): PNode =
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(getIdent(c.cache, it), arg.info))
if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}:
result.add newDeref(arg)
else:
result.add arg
result = semExprNoDeref(c, result, flags + {efWantIterator})
result = semExprNoDeref(c, result, {efWantIterator})
proc isTrivalStmtExpr(n: PNode): bool =
for i in 0..<n.len-1:
@@ -1293,8 +1289,7 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
if result != nil: return result
openScope(c)
result = n
let iteratorFlags = flags * {efPreferIteratorForIterable}
n[^2] = semExprNoDeref(c, n[^2], iteratorFlags + {efWantIterator})
n[^2] = semExprNoDeref(c, n[^2], {efWantIterator})
var call = n[^2]
if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)):
@@ -1314,16 +1309,14 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
elif not isCallExpr or call[0].kind != nkSym or
call[0].sym.kind != skIterator:
if n.len == 3:
n[^2] = implicitIterator(c, "items", n[^2], iteratorFlags)
n[^2] = implicitIterator(c, "items", n[^2])
elif n.len == 4:
n[^2] = implicitIterator(c, "pairs", n[^2], iteratorFlags)
n[^2] = implicitIterator(c, "pairs", n[^2])
else:
localError(c.config, n[^2].info, "iterator within for loop context expected")
result = semForVars(c, n, flags)
else:
result = semForVars(c, n, flags)
if n[^2].typ != nil and n[^2].typ.kind == tyIterable:
n[^2].typ = n[^2].typ.skipModifier
# propagate any enforced VoidContext:
if n[^1].typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
@@ -2559,9 +2552,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if not hasProto:
implicitPragmas(c, s, n.info, validPragmas)
if {sfError, sfExportc} * s.flags == {sfError, sfExportc}:
localError(c.config, n.info, "{.error.} and {.exportc.} pragmas are incompatible")
if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags:
setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s)
s.typ.incl tfEffectSystemWorkaround

View File

@@ -2234,9 +2234,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semAnyRef(c, n, tyPtr, prev)
elif op.id == ord(wRef):
result = semAnyRef(c, n, tyRef, prev)
elif op.id == ord(wStatic):
checkSonsLen(n, 2, c.config)
result = semStaticType(c, n[1], prev)
elif op.id == ord(wType):
checkSonsLen(n, 2, c.config)
result = semTypeOf(c, n[1], prev)

View File

@@ -46,8 +46,7 @@ type
TCandidate* = object
c*: PContext
exactMatches*: int
iteratorPreference*: int # prefer iterators in iterator-oriented contexts
exactMatches*: int # also misused to prefer iters over procs
genericMatches: int # also misused to prefer constraints
subtypeMatches: int
intConvMatches: int # conversions to int are not as expensive
@@ -111,8 +110,7 @@ proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
proc initCandidateAux(ctx: PContext,
callee: PType): TCandidate {.inline.} =
result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0,
iteratorPreference: 0, convMatches: 0, intConvMatches: 0,
genericMatches: 0,
convMatches: 0, intConvMatches: 0, genericMatches: 0,
state: csEmpty, firstMismatch: MismatchInfo(),
callee: callee, call: nil, baseTypeMatch: false,
genericConverter: false, inheritancePenalty: -1
@@ -395,7 +393,6 @@ proc complexDisambiguation(a, b: PType): int =
proc writeMatches*(c: TCandidate) =
echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info
echo " exact matches: ", c.exactMatches
echo " iterator preference: ", c.iteratorPreference
echo " generic matches: ", c.genericMatches
echo " subtype matches: ", c.subtypeMatches
echo " intconv matches: ", c.intConvMatches
@@ -414,8 +411,6 @@ proc cmpInheritancePenalty(a, b: int): int =
proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
result = a.exactMatches - b.exactMatches
if result != 0: return
result = a.iteratorPreference - b.iteratorPreference
if result != 0: return
result = a.genericMatches - b.genericMatches
if result != 0: return
result = a.subtypeMatches - b.subtypeMatches
@@ -2753,8 +2748,7 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool):
result = a
elif a.typ.isNil:
if formal.kind == tyIterable:
let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable,
efPreferIteratorForIterable}
let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable}
result = c.semOperand(c, a, flags)
else:
# XXX This is unsound! 'formal' can differ from overloaded routine to
@@ -2771,20 +2765,6 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool):
considerGenSyms(c, result)
if result.kind != nkHiddenDeref and result.typ.kind in {tyVar, tyLent} and c.matchedConcept == nil:
result = newDeref(result)
# Recovery for calls resolved too early as non-iterators.
# TODO: retry only skIterator overloads instead of re-semming,
# or preserve iterator-candidates info from the earlier semcheck.
if formal.kind == tyIterable and result.typ.kind != tyIterable and
a.kind in nkCallKinds and a[0].kind in {nkIdent, nkAccQuoted, nkSym, nkOpenSym}:
let recheck = copyTree(a)
recheck.typ = nil
if recheck[0].kind == nkSym and recheck[0].sym != nil:
recheck[0] = newIdentNode(recheck[0].sym.name, recheck[0].info)
let flags = {efDetermineType, efAllowStmt, efNoUndeclared,
efWantIterator, efWantIterable, efPreferIteratorForIterable}
let fresh = c.semOperand(c, recheck, flags)
if fresh.typ != nil and fresh.typ.kind == tyIterable:
return fresh
proc prepareOperand(c: PContext; a: PNode, newlyTyped: var bool): PNode =
if a.typ.isNil:

View File

@@ -118,21 +118,6 @@ proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite
le.flags.incl nfFirstWrite
result[1] = ri
proc resolveBorrowedRoutineSym(c: PTransf; s: PSym; info: TLineInfo): PSym =
# Follow borrow aliases to the underlying implementation symbol.
result = nil
var s = s
while true:
# Skips over all borrowed procs getting the last proc symbol without an implementation.
let body = getBody(c.graph, s)
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
s = body.sym
else:
if body.kind != nkSym:
internalError(c.graph.config, info, "wrong AST for borrowed symbol")
return body.sym
internalError(c.graph.config, info, "wrong AST for borrowed symbol")
proc transformSymAux(c: PTransf, n: PNode): PNode =
let s = n.sym
if s.typ != nil and s.typ.callConv == ccClosure:
@@ -151,7 +136,17 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
var tc = c.transCon
if sfBorrow in s.flags and s.kind in routineKinds:
# simply exchange the symbol:
b = newSymNode(resolveBorrowedRoutineSym(c, s, n.info), n.info)
var s = s
while true:
# Skips over all borrowed procs getting the last proc symbol without an implementation
let body = getBody(c.graph, s)
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
s = body.sym
else:
break
b = getBody(c.graph, s)
if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym, n.info)
elif c.inlining > 0:
# see bug #13596: we use ref-based equality in the DFA for destruction
# injections so we need to ensure unique nodes after iterator inlining
@@ -790,9 +785,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
discard c.breakSyms.pop
var iter = call[0].sym
if sfBorrow in iter.flags and iter.kind in routineKinds:
iter = resolveBorrowedRoutineSym(c, iter, n.info)
let iter = call[0].sym
var v = newNodeI(nkVarSection, n.info)
for i in 0..<n.len - 2:
@@ -1197,13 +1190,6 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
# no need to transform type sections:
return n
of nkVarSection, nkLetSection:
# NIF loads let/var sections with bare nkSym children instead of nkIdentDefs.
# Expand them so transformSons reaches the value expression (e.g. for-loop).
for i in 0 ..< n.len:
if n[i].kind == nkSym:
let impl = n[i].sym.ast # triggers lazy load if Partial
if impl != nil and impl.kind == nkIdentDefs:
n[i] = impl
if c.inlining > 0:
# we need to copy the variables for multiple yield statements:
result = transformVarSection(c, n)

View File

@@ -99,13 +99,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
# only closure iterators may be assigned to anything.
result = t
let innerFlags = flags - {taObjField, taTupField, taIsOpenArray}
let f = if kind in {skProc, skFunc}: innerFlags+{taNoUntyped} else: innerFlags
let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
for _, a in t.paramTypes:
if result != nil: break
result = typeAllowedAux(marker, a, skParam, c, f)
result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray})
if result.isNil and t.returnType != nil:
result = typeAllowedAux(marker, t.returnType, skResult, c, innerFlags)
result = typeAllowedAux(marker, t.returnType, skResult, c, flags)
of tyTypeDesc:
if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
result = t

View File

@@ -274,7 +274,7 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef
c.typeKey(t.sonsImpl[0], flags-{CoIgnoreRange}, conf)
else:
withTree c.m, toNifTag(t.kind):
for i in 0..<t.sonsImpl.len:
for i in 1..<t.sonsImpl.len:
c.typeKey t.sonsImpl[i], flags, conf
if tfNotNil in t.flagsImpl and CoType notin flags:
c.m.addIdent "´notnil"

View File

@@ -1779,7 +1779,3 @@ proc reduceToBase*(f: PType): PType =
result = f.elementType
else:
result = f
proc supportsCopyMem*(t: PType): bool =
let t = t.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
result = not containsGarbageCollectedRef(t) and not hasDestructor(t)

View File

@@ -62,10 +62,7 @@ proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
result = newNodeI(nkIdentDefs, n.info)
result.add n # name
result.add mapTypeToAstX(cache, n.sym.typ, n.info, idgen, true, false) # type
if n.sym.ast != nil:
result.add copyTree(n.sym.ast)
else:
result.add newNodeI(nkEmpty, n.info) # no assigned value
result.add newNodeI(nkEmpty, n.info) # no assigned value
else:
result = copyNode(n)
for i in 0..<n.safeLen:
@@ -90,10 +87,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
var id = newNodeX(nkIdentDefs)
id.add n # name
id.add mapTypeToAst(t, info) # type
if n.sym.ast != nil:
id.add copyTree(n.sym.ast)
else:
id.add newNodeI(nkEmpty, n.info) # no assigned value
id.add newNodeI(nkEmpty, info) # no assigned value
id
template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)

View File

@@ -1726,9 +1726,6 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if sameBackendType(le.typ, le[1].typ):
genAsgn(c, le[1], ri, requiresCopy)
of nkStmtListExpr:
for i in 0..<le.len-1: gen(c, le[i])
genAsgn(c, le[^1], ri, requiresCopy)
else:
let dest = c.genx(le, {gfNodeAddr})
genAsgn(c, dest, ri, requiresCopy)

View File

@@ -248,9 +248,6 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">$title</h1>$subtitle
$content

View File

@@ -735,24 +735,6 @@ with a hyperlink to your own code repository.
In the case of Nim's own documentation, the `commit` value is just a commit
hash to append to a formatted URL to https://github.com/nim-lang/Nim.
Substitution via environment variables
--------------------------------------
A simple substitution using environment variables is available.
A reference written as ``|name|`` is replaced during documentation generation if
a matching variable is provided. You can define it via the compiler with
``--putenv``. This is useful for injecting values like version strings or
build-specific text.
```nim
## |foo|
```
```cmd
nim --putenv:foo=bar doc filename.nim
```
The generated html will contain ``bar`` instead of ``foo``.
Other Input Formats
===================

View File

@@ -2628,10 +2628,10 @@ Overload resolution
In a call `p(args)` where `p` may refer to more than one
candidate, it is said to be a symbol choice. Overload resolution will attempt to
find the best candidate, thus transforming the symbol choice into a resolved symbol.
The routine `p` that matches best is selected following a series of trials explained below.
The routine `p` that matches best is selected following a series of trials explained below.
In order: Category matching, Hierarchical Order Comparison, and finally, Complexity Analysis.
If multiple candidates match equally well after all trials have been tested, the ambiguity
If multiple candidates match equally well after all trials have been tested, the ambiguity
is reported during semantic analysis.
First Trial: Category matching
@@ -2664,7 +2664,7 @@ resolved symbol.
For example, if a candidate with one exact match is compared to a candidate with multiple
generic matches and zero exact matches, the candidate with an exact match will win.
Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number
Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number
of matches of the matching category `m` for the routine `p`.
A routine `p` matches better than a routine `q` if the following
@@ -2692,11 +2692,11 @@ type A[T] = object
```
Matching formals for this type include `T`, `object`, `A`, `A[...]` and `A[C]` where `C` is a concrete type, `A[...]`
is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of
is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of
specificity with respect to `A` as each subsequent category narrows the set of types that are members of their match set.
In this trial, the formal parameters of candidates are compared in order (1st parameter, 2nd parameter, etc.) to search for
a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen
a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen
as the resolved symbol.
Third Trial: Complexity Analysis
@@ -2951,13 +2951,13 @@ proc sort*[I: Index; T: Comparable](x: var Indexable[I, T])
In the above example, `Comparable` and `Indexable` are types that will match any type that
can can bind each definition declared in the concept body. The special `Self` type defined
in the concept body refers to the type being matched, also called the "implementation" of
the concept. Implementations that match the concept are generic matches, and the concept
in the concept body refers to the type being matched, also called the "implementation" of
the concept. Implementations that match the concept are generic matches, and the concept
typeclasses themselves work in a similar way to generic type variables in that they are never
concrete types themselves (even if they have concrete type parameters such as `Indexable[int, int]`)
and expressions like `typeof(x)` in the body of `proc sort` from the above example will return the
and expressions like `typeof(x)` in the body of `proc sort` from the above example will return the
type of the implementation, not the concept typeclass. Concepts are useful for providing information
to the compiler in generic contexts, most notably for generic type checking, and as a tool for
to the compiler in generic contexts, most notably for generic type checking, and as a tool for
[Overload resolution]. Generic type checking is forthcoming, so this will only explain overload
resolution for now.
@@ -2984,7 +2984,7 @@ Concept overload resolution
When an operand's type is being matched to a concept, the operand's type is set as the "potential
implementation". For each definition in the concept body, overload resolution is performed by substituting `Self`
for the potential implementation to try and find a match for each definition. If this succeeds, the concept
for the potential implementation to try and find a match for each definition. If this succeeds, the concept
matches. Implementations do not need to exactly match the definitions in the concept. For example:
```nim
@@ -3008,7 +3008,7 @@ This leads to confusing and impractical behavior in most situations, so the rule
1. if a concept is being compared with `T` or any type that accepts all other types (`auto`) the concept
is more specific
2. if the concept is being compared with another concept the result is deferred to [Concept subset matching]
3. in any other case the concept is less specific then it's competitor
3. in any other case the concept is less specific then it's competitor
Currently, the concept evaluation mechanism evaluates to a successful match on the first acceptable candidate
for each defined binding. This has a couple of notable effects:
@@ -4610,10 +4610,10 @@ for any type (with some exceptions) by defining a routine with the name `[]`.
```nim
type Foo = object
data: seq[int]
proc `[]`(foo: Foo, i: int): int =
result = foo.data[i]
let foo = Foo(data: @[1, 2, 3])
echo foo[1] # 2
```
@@ -4624,12 +4624,12 @@ which has precedence over assigning to the result of `[]`.
```nim
type Foo = object
data: seq[int]
proc `[]`(foo: Foo, i: int): int =
result = foo.data[i]
proc `[]=`(foo: var Foo, i: int, val: int) =
foo.data[i] = val
var foo = Foo(data: @[1, 2, 3])
echo foo[1] # 2
foo[1] = 5
@@ -4861,14 +4861,7 @@ default to being inline, but this may change in future versions of the
implementation.
The `iterator` type is always of the calling convention `closure`
implicitly.
Unlike named iterators, anonymous iterator expressions evaluate
to the `iterator` type. In practice, this means a named iterator declaration
without `{.closure.}` defaults to inline, but an expression like `let it =
iterator(): int = yield 1` produces a callable closure iterator value.
The following example shows how to use iterators to implement
implicitly; the following example shows how to use iterators to implement
a `collaborative tasking`:idx: system:
```nim
@@ -6408,7 +6401,7 @@ The default for symbols of entity `type`, `var`, `let` and `const`
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
is defined in the same syntax-level scope, it will be `gensym` by default.
This can be overridden by marking the routine as `inject`.
This can be overridden by marking the routine as `inject`.
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
@@ -7249,7 +7242,7 @@ identifier is considered ambiguous, which can be resolved in the following ways:
write(stdout, x) # error: x is ambiguous
write(stdout, A.x) # no error: qualifier used
proc bar(a: int): int = a + 1
assert bar(x) == x + 1 # no error: only A.x of type int matches
@@ -9331,3 +9324,4 @@ It is not valid to pass an lvalue of a supertype to an `out T` parameter:
However, in the future this could be allowed and provide a better way to write object
constructors that take inheritance into account.

View File

@@ -123,6 +123,7 @@ Modified by Boyd Greenfield and narimiran
}
html {
overflow-x: hidden;
max-width: 100%;
box-sizing: border-box;
font-size: 100%;
@@ -155,8 +156,7 @@ body {
margin-left: 1%; }
@media print {
#global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby,
#nav-burger, #nav-overlay, .three.columns {
#global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {
display:none;
}
.columns {
@@ -175,7 +175,6 @@ body {
height: 100vh;
position: sticky;
top: 0px;
left: 0px;
overflow-y: auto;
padding: 2px;
}
@@ -189,67 +188,9 @@ body {
width: 100%;
margin-left: 0; }
#nav-burger, #nav-overlay {
display: none;
}
@media screen and (max-width: 860px) {
#nav-burger {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0.25em;
left: 0.25em;
z-index: 200;
width: 1.6rem;
height: 1.6rem;
font-size: 1.25em;
cursor: pointer;
border-radius: 4px;
background-color: var(--secondary-background);
color: var(--text);
border: 1px solid var(--border);
user-select: none;
opacity: 0.55;
}
#nav-burger:hover {
background-color: var(--third-background);
}
#nav-toggle:checked ~ .container .three.columns {
transform: translateX(0);
}
#nav-toggle:checked ~ #nav-overlay {
opacity: 1;
pointer-events: auto;
}
#nav-overlay {
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 99; /* below sidebar */
background: rgba(0, 0, 0, 0.35);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease;
}
.three.columns {
display: block;
position: fixed;
left: 0;
width: min(80vw, 24em);
padding-top: 1.6em;
height: 100vh; /* Fallback */
height: 100dvh;
overflow-y: auto;
z-index: 100;
background-color: var(--secondary-background);
box-shadow: 2px 0 12px rgba(0,0,0,0.25);
transform: translateX(-110%);
transition: transform 0.25s ease;
display: none;
}
.nine.columns {
width: 100%;
@@ -259,8 +200,6 @@ body {
body {
font-size: 1em;
line-height: 1.35;
margin-left: 0.35em;
margin-right: 0.35em;
}
}
@@ -419,10 +358,6 @@ img {
h1.title {
page-break-before: avoid; }
.nine.columns h1:first-of-type {
page-break-before: avoid;
}
p, h2, h3 {
orphans: 3;
@@ -490,22 +425,6 @@ h5 {
h6 {
font-size: 1.1em; }
@media screen and (max-width: 860px) {
h1.title {
font-size: 2em;
}
h1 {
font-size: 1.5em;
margin-top: 1.5em;
margin-bottom: 0.75em;
}
h2 {
margin-top: 1.3em;
}
h3 {
margin-top: 1.2em;
}
}
ul, ol {
padding: 0;
@@ -653,8 +572,8 @@ blockquote.markdown-quote {
padding-left: 3px;
padding-right: 3px;
border-radius: 4px;
white-space: pre-wrap;
overflow-wrap: break-word;
white-space: normal;
word-break: break-all;
}
span.tok {
@@ -689,15 +608,6 @@ pre {
border-radius: 6px;
}
@media screen and (max-width: 860px) {
pre {
font-stretch: semi-condensed;
letter-spacing: -0.25px;
line-height: 1.25;
padding: 0.33em;
}
}
.copyToClipBoardBtn {
visibility: hidden;
position: absolute;
@@ -764,8 +674,6 @@ table {
border-collapse: collapse;
border-color: var(--third-background);
border-spacing: 0;
display: block;
overflow-x: auto;
}
table:not(.line-nums-table) {

View File

@@ -11,12 +11,12 @@
const
# examples of possible values for repos: Head, ea82b54
NimbleStableCommit = "aa03f886e4a111d6af9090c6a1f1271d64b66f7b" # 0.22.2
NimbleStableCommit = "9207e8b2bbdf66b5a4d1020214cff44d2d30df92" # 0.20.1
AtlasStableCommit = "ff1f4289482dce94ba9f95b3b0ae16d16e21eb3d" # 0.10.1
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
NimonyStableCommit = "bbfb21529845567c55b67d176354daef0e7d6c29" # unversioned \
NimonyStableCommit = "deb9b50c573fb55e071825ab55385e293b7216d5" # 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.

View File

@@ -326,8 +326,7 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
pos = nimNumber(g, pos)
of '\'':
inc(pos)
let followsBacktick = pos >= 2 and g.buf[pos - 2] == '`'
if not followsBacktick:
if g.kind != gtPunctuation:
g.kind = gtCharLit
while true:
case g.buf[pos]
@@ -339,8 +338,6 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
of '\\':
inc(pos, 2)
else: inc(pos)
else:
g.kind = gtPunctuation
of '\"':
inc(pos)
if (g.buf[pos] == '\"') and (g.buf[pos + 1] == '\"'):

View File

@@ -1092,7 +1092,7 @@ template mapIt*(s: typed, op: untyped): untyped =
type OutType = typeof((
block:
var it{.inject, used.}: typeof(items(s), typeOfIter);
var it{.inject.}: typeof(items(s), typeOfIter);
op), typeOfProc)
when OutType is not (proc):
# Here, we avoid to create closures in loops.

View File

@@ -130,7 +130,6 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
var a = initHashSet[int]()
a.incl(3)
assert len(a) == 1
result = default(HashSet[A])
result.init(initialSize)
@@ -140,7 +139,7 @@ proc `[]`*[A](s: var HashSet[A], key: A): var A =
##
## This is useful when one overloaded `hash` and `==` but still needs
## reference semantics for sharing.
var hc = default(Hash)
var hc: Hash
var index = rawGet(s, key, hc)
if index >= 0: result = s.data[index].key
else:
@@ -166,7 +165,7 @@ proc contains*[A](s: HashSet[A], key: A): bool =
assert values.contains(2)
assert 2 in values
var hc = default(Hash)
var hc: Hash
var index = rawGet(s, key, hc)
result = index >= 0
@@ -671,7 +670,6 @@ proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] =
var a = initOrderedSet[int]()
a.incl(3)
assert len(a) == 1
result = OrderedSet[A]()
result.init(initialSize)
@@ -712,7 +710,7 @@ proc contains*[A](s: OrderedSet[A], key: A): bool =
assert values.contains(2)
assert 2 in values
var hc = default(Hash)
var hc: Hash
var index = rawGet(s, key, hc)
result = index >= 0
@@ -891,6 +889,8 @@ proc `$`*[A](s: OrderedSet[A]): string =
## ```
dollarImpl()
iterator items*[A](s: OrderedSet[A]): A =
## Iterates over keys in the ordered set `s` in insertion order.
##

View File

@@ -65,9 +65,7 @@ proc fillBuffer(L: var BaseLexer) =
L.buf[i] = L.buf[L.sentinel + 1 + i]
else:
# "moveMem" handles overlapping regions
let p = beginStore(L.buf, L.buf.len)
moveMem(p, addr p[L.sentinel + 1], toCopy)
endStore(L.buf)
moveMem(addr L.buf[0], addr L.buf[L.sentinel + 1], toCopy)
charsRead = L.input.readDataStr(L.buf, toCopy ..< toCopy + L.sentinel + 1)
s = toCopy + charsRead
if charsRead < L.sentinel + 1:

View File

@@ -921,7 +921,7 @@ elif not defined(useNimRtl):
for key, val in pairs(t):
var x = key & "=" & val
result[i] = cast[cstring](alloc(x.len+1))
copyMem(result[i], x.cstring, x.len+1)
copyMem(result[i], addr(x[0]), x.len+1)
inc(i)
proc envToCStringArray(): cstringArray =
@@ -932,7 +932,7 @@ elif not defined(useNimRtl):
for key, val in envPairs():
var x = key & "=" & val
result[i] = cast[cstring](alloc(x.len+1))
copyMem(result[i], x.cstring, x.len+1)
copyMem(result[i], addr(x[0]), x.len+1)
inc(i)
type

View File

@@ -556,7 +556,7 @@ proc replace(s: string): string =
while i < s.len():
if s[i] == '\\':
d.add(r"\\")
elif s[i] == '\c' and i+1 < s.len() and s[i+1] == '\l':
elif s[i] == '\c' and s[i+1] == '\l':
d.add(r"\c\l")
inc(i)
elif s[i] == '\c':

View File

@@ -259,8 +259,10 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
result = s.readDataStrImpl(s, buffer, slice)
else:
# fallback
result = s.readData(beginStore(buffer, slice.b + 1 - slice.a, slice.a), slice.b + 1 - slice.a)
endStore(buffer)
when declared(prepareMutation):
# buffer might potentially be a CoW literal with ARC
prepareMutation(buffer)
result = s.readData(addr buffer[slice.a], slice.b + 1 - slice.a)
template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
when nimvm:
@@ -1226,8 +1228,7 @@ else: # after 1.3 or JS not defined
jsOrVmBlock:
buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
do:
copyMem(beginStore(buffer, result, slice.a), readRawData(s.data, s.pos), result)
endStore(buffer)
copyMem(unsafeAddr buffer[slice.a], addr s.data[s.pos], result)
inc(s.pos, result)
else:
result = 0
@@ -1243,7 +1244,7 @@ else: # after 1.3 or JS not defined
raise newException(Defect, "could not read string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(buffer, readRawData(s.data, s.pos), result)
copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result)
else:
result = 0
@@ -1259,7 +1260,7 @@ else: # after 1.3 or JS not defined
raise newException(Defect, "could not peek string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(buffer, readRawData(s.data, s.pos), result)
copyMem(buffer, addr(s.data[s.pos]), result)
else:
result = 0
@@ -1276,8 +1277,7 @@ else: # after 1.3 or JS not defined
raise newException(Defect, "could not write to string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(beginStore(s.data, bufLen, s.pos), buffer, bufLen)
endStore(s.data)
copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen)
proc ssClose(s: Stream) =
@@ -1345,9 +1345,7 @@ proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
result = readBuffer(FileStream(s).f, buffer, bufLen)
proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
let len = slice.b + 1 - slice.a
result = readBuffer(FileStream(s).f, beginStore(buffer, len, slice.a), len)
endStore(buffer)
result = readBuffer(FileStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a)
proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
let pos = fsGetPosition(s)

View File

@@ -1983,10 +1983,9 @@ func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl,
when hasCStringBuiltin:
let length = last-start+1
if length > 0:
let sdata = readRawData(s)
let found = c_memchr(addr sdata[start], cint(sub), cast[csize_t](length))
let found = c_memchr(s[start].unsafeAddr, cint(sub), cast[csize_t](length))
if not found.isNil:
return cast[int](found) -% cast[int](sdata)
return cast[int](found) -% cast[int](s.cstring)
else:
findImpl()
@@ -2042,10 +2041,9 @@ func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl,
when declared(memmem):
let subLen = sub.len
if last < 0 and start < s.len and subLen != 0:
let sdata = readRawData(s)
let found = memmem(addr sdata[start], csize_t(s.len - start), readRawData(sub), csize_t(subLen))
let found = memmem(s[start].unsafeAddr, csize_t(s.len - start), sub.cstring, csize_t(subLen))
result = if not found.isNil:
cast[int](found) -% cast[int](sdata)
cast[int](found) -% cast[int](s.cstring)
else:
-1
else:

View File

@@ -264,11 +264,7 @@ elif defined(windows):
tm_yday*: cint ## Day of year [0,365].
tm_isdst*: cint ## Daylight Savings flag.
# Prefer 64-bit version always - time_t might be 32 or 64 bit depending on
# the setting of _USE_32BIT_TIME_T and we have no way of detecting which
# version is actually used by default:
# https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64
proc localtime(a1: var CTime): ptr Tm {.importc: "_localtime64", header: "<time.h>", sideEffect.}
proc localtime(a1: var CTime): ptr Tm {.importc, header: "<time.h>", sideEffect.}
type
Month* = enum ## Represents a month. Note that the enum starts at `1`,

View File

@@ -19,12 +19,7 @@ proc addCstringN(result: var string, buf: cstring; buflen: int) =
let oldLen = result.len
let newLen = oldLen + buflen
result.setLen newLen
{.cast(noSideEffect).}:
when declared(completeStore):
c_memcpy(beginStore(result, buflen, oldLen), buf, buflen.csize_t)
endStore(result)
else:
discard c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
import std/private/[dragonbox, schubfach]

View File

@@ -52,7 +52,7 @@ func addChars[T](result: var string, x: T, start: int, n: int) {.inline, enforce
for i in 0..<n: result[old + i] = x[start + i]
when nimvm: impl
else:
when defined(js) or defined(nimscript) or defined(nimsso): impl
when defined(js) or defined(nimscript): impl
else:
{.noSideEffect.}:
copyMem result[old].addr, x[start].unsafeAddr, n

View File

@@ -84,9 +84,9 @@ func setSlice*(s: var string, slice: Slice[int]) =
when not declared(moveMem):
impl()
else:
let p = beginStore(s, last - first + 1)
moveMem(p, addr p[first], last - first + 1)
endStore(s)
when defined(nimSeqsV2):
prepareMutation(s)
moveMem(addr s[0], addr s[first], last - first + 1)
s.setLen(last - first + 1)
func strip*(a: var string, leading = true, trailing = true, chars: set[char] = whitespaces) {.inline.} =

View File

@@ -485,8 +485,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
while true:
# fixes #9634; this pattern may need to be abstracted as a template if reused;
# likely other io procs need this for correctness.
fgetsSuccess = c_fgets(cast[cstring](beginStore(line, sp, pos)), sp.cint, f) != nil
endStore(line)
fgetsSuccess = c_fgets(cast[cstring](addr line[pos]), sp.cint, f) != nil
if fgetsSuccess: break
when not defined(nimscript):
if errno == EINTR:
@@ -496,11 +495,10 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
checkErr(f)
break
let lineData = readRawData(line)
let m = c_memchr(addr lineData[pos], cint('\L'), cast[csize_t](sp))
let m = c_memchr(addr line[pos], cint('\L'), cast[csize_t](sp))
if m != nil:
# \l found: Could be our own or the one by fgets, in any case, we're done
var last = cast[int](m) - cast[int](lineData)
var last = cast[int](m) - cast[int](addr line[0])
if last > 0 and line[last-1] == '\c':
line.setLen(last-1)
return last > 1 or fgetsSuccess
@@ -566,8 +564,7 @@ proc readAllBuffer(file: File): string =
result = ""
var buffer = newString(BufSize)
while true:
var bytesRead = readBuffer(file, beginStore(buffer, BufSize), BufSize)
endStore(buffer)
var bytesRead = readBuffer(file, addr(buffer[0]), BufSize)
if bytesRead == BufSize:
result.add(buffer)
else:
@@ -593,8 +590,7 @@ proc readAllFile(file: File, len: int64): string =
# We acquire the filesize beforehand and hope it doesn't change.
# Speeds things up.
result = newString(len)
let bytes = readBuffer(file, beginStore(result, len.int), len.int)
endStore(result)
let bytes = readBuffer(file, addr(result[0]), len)
if endOfFile(file):
if bytes.int64 < len:
result.setLen(bytes)

View File

@@ -13,11 +13,11 @@ when defined(nimdoc):
Time* = Impl ## \
## Wrapper for `time_t`. On posix, this is an alias to `posix.Time`.
elif defined(windows):
# Unless _USE_32BIT_TIME_T is defined, time_t is a 64-bit value on both 32
# and 64-bit versions of windows:
# https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/time-time32-time64
# For the avoidance of doubt, always use 64-bit version
type Time* {.importc: "__time64_t", header: "<time.h>".} = distinct clonglong
when defined(i386) and defined(gcc):
type Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
else:
# newest version of Visual C++ defines time_t to be of 64 bits
type Time* {.importc: "time_t", header: "<time.h>".} = distinct int64
elif defined(posix):
import std/posix
export posix.Time

View File

@@ -1469,7 +1469,7 @@ when defined(nimHasTopDownInference):
## This is not as efficient as turning a fixed length array into a sequence
## as it always copies every element of `a`.
let sz = a.len
when supportsCopyMem(T) and not defined(js) and not defined(nimscript):
when supportsCopyMem(T) and not defined(js):
result = newSeqUninit[T](sz)
when nimvm:
for i in 0..sz-1: result[i] = a[i]
@@ -1622,29 +1622,26 @@ when notJSnotNims:
include system/sysmem
when notJSnotNims and defined(nimSeqsV2):
when defined(nimsso):
const nimStrVersion {.core.} = 3
else:
const nimStrVersion {.core.} = 2
const nimStrVersion {.core.} = 2
type
NimStrPayloadBase = object
cap: int
type
NimStrPayloadBase = object
cap: int
NimStrPayload {.core.} = object
cap: int
data: UncheckedArray[char]
NimStrPayload {.core.} = object
cap: int
data: UncheckedArray[char]
NimStringV2 {.core.} = object
len: int
p: ptr NimStrPayload ## can be nil if len == 0.
NimStringV2 {.core.} = object
len: int
p: ptr NimStrPayload ## can be nil if len == 0.
when defined(windows):
proc GetLastError(): int32 {.header: "<windows.h>", nodecl.}
const ERROR_BAD_EXE_FORMAT = 193
when notJSnotNims:
when defined(nimSeqsV2) and not defined(nimsso):
when defined(nimSeqsV2):
proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.}
when hostOS != "standalone" and hostOS != "any":
@@ -1692,32 +1689,9 @@ when not defined(nimIcIntegrityChecks):
export exceptions
when notJSnotNims and defined(nimSeqsV2):
when defined(nimsso):
include "system/strs_v3"
else:
include "system/strs_v2"
include "system/strs_v2"
include "system/seqs_v2"
when not (notJSnotNims and defined(nimSeqsV2)):
# Fallback implementations for backends where strs_v2/v3 is not included.
# Needed so modules imported by system (e.g. syncio) can reference these without guards.
when notJSnotNims:
# mm:refc: string = ptr NimStringDesc with data: UncheckedArray[char]
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
let ns = cast[NimString](s)
if ns == nil: nil
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = discard
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] =
let ns = cast[NimString](s)
if ns == nil: nil
else: cast[ptr UncheckedArray[char]](addr ns.data[start])
else:
# JS/nimscript: callers are guarded by whenNotVmJsNims/when not defined(js)
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = nil
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = discard
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] = nil
when not defined(js):
template newSeqImpl(T, len) =
result = newSeqOfCap[T](len)
@@ -1767,9 +1741,6 @@ when not defined(js):
else:
{.error: "The type T cannot contain managed memory or have destructors".}
when defined(nimsso) and not declared(newStringUninitWasDeclared):
proc newStringUninitImpl(len: Natural): string {.noSideEffect, inline.}
proc newStringUninit*(len: Natural): string {.noSideEffect.} =
## Returns a new string of length `len` but with uninitialized
## content. One needs to fill the string character after character
@@ -1780,20 +1751,17 @@ when not defined(js):
when nimvm:
result = newString(len)
else:
when defined(nimsso):
result = newStringUninitImpl(len)
else:
result = newStringOfCap(len)
{.cast(noSideEffect).}:
when defined(nimSeqsV2):
let s = cast[ptr NimStringV2](addr result)
if len > 0:
s.len = len
s.p.data[len] = '\0'
else:
let s = cast[NimString](result)
result = newStringOfCap(len)
{.cast(noSideEffect).}:
when defined(nimSeqsV2):
let s = cast[ptr NimStringV2](addr result)
if len > 0:
s.len = len
s.data[len] = '\0'
s.p.data[len] = '\0'
else:
let s = cast[NimString](result)
s.len = len
s.data[len] = '\0'
else:
proc newStringUninit*(len: Natural): string {.
magic: "NewString", importc: "mnewString", noSideEffect.}
@@ -2276,13 +2244,10 @@ when not defined(js) or defined(nimscript):
else: result = 0
else:
when not defined(nimscript): # avoid semantic checking
when defined(nimsso):
result = cmpStrings(x, y)
else:
let minlen = min(x.len, y.len)
result = int(nimCmpMem(x.cstring, y.cstring, cast[csize_t](minlen)))
if result == 0:
result = x.len - y.len
let minlen = min(x.len, y.len)
result = int(nimCmpMem(x.cstring, y.cstring, cast[csize_t](minlen)))
if result == 0:
result = x.len - y.len
when declared(newSeq):
proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
@@ -2948,9 +2913,7 @@ proc substr*(a: openArray[char]): string =
result = newStringUninit(a.len)
whenNotVmJsNims():
if a.len > 0:
{.cast(noSideEffect).}:
copyMem(beginStore(result, a.len), a[0].unsafeAddr, a.len)
endStore(result)
copyMem(result[0].addr, a[0].unsafeAddr, a.len)
do:
for i, ch in a:
result[i] = ch
@@ -2985,8 +2948,7 @@ proc substr*(s: string; first, last: int): string = # A bug with `magic: Slice`
result = newStringUninit(L)
whenNotVmJsNims():
if L > 0:
copyMem(beginStore(result, L), readRawData(s, first), L)
endStore(result)
copyMem(result[0].addr, s[first].unsafeAddr, L)
do:
for i in 0..<L:
result[i] = s[i + first]
@@ -3204,6 +3166,3 @@ when hostOS == "standalone":
# ssymbols being duplicated.
proc nimPanic(s: string) {.exportc, noreturn.} = panic(s)
proc nimRawoutput(s: string) {.exportc.} = rawoutput(s)
when not declared(newStringUninitWasDeclared):
proc newStringUninitImpl(len: Natural): string {.noSideEffect, inline.} = discard

View File

@@ -306,15 +306,15 @@ proc `mod`*(x, y: uint32): uint32 {.magic: "ModU", noSideEffect.}
proc `mod`*(x, y: uint64): uint64 {.magic: "ModU", noSideEffect.}
proc `+=`*[T: SomeInteger](x: var T, y: T) {.
magic: "Inc", noSideEffect, systemRaisesDefect.}
magic: "Inc", noSideEffect.}
## Increments an integer.
proc `-=`*[T: SomeInteger](x: var T, y: T) {.
magic: "Dec", noSideEffect, systemRaisesDefect.}
magic: "Dec", noSideEffect.}
## Decrements an integer.
proc `*=`*[T: SomeInteger](x: var T, y: T) {.
inline, noSideEffect, systemRaisesDefect.} =
inline, noSideEffect.} =
## Binary `*=` operator for integers.
x = x * y
@@ -339,22 +339,20 @@ proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
x = x + y
proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
inline, noSideEffect, systemRaisesDefect.} =
inline, noSideEffect.} =
## Decrements in place a floating point number.
x = x - y
proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.
inline, noSideEffect, systemRaisesDefect.} =
inline, noSideEffect.} =
## Multiplies in place a floating point number.
x = x * y
proc `/=`*(x: var float64, y: float64) {.
inline, noSideEffect, systemRaisesDefect.} =
proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} =
## Divides in place a floating point number.
x = x / y
proc `/=`*[T: float|float32](x: var T, y: T) {.
inline, noSideEffect, systemRaisesDefect.} =
proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} =
## Divides in place a floating point number.
x = x / y

View File

@@ -62,14 +62,9 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
case mt.kind
of tyString:
when defined(nimSeqsV2):
when defined(nimsso):
var x = cast[ptr SmallString](dest)
var s2 = cast[ptr SmallString](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]
@@ -250,11 +245,8 @@ proc genericReset(dest: pointer, mt: PNimType) =
unsureAsgnRef(cast[PPointer](dest), nil)
of tyString:
when defined(nimSeqsV2):
when defined(nimsso):
nimDestroyStrV1(cast[ptr SmallString](dest)[])
else:
var s = cast[ptr NimStringV2](dest)
frees(s[])
var s = cast[ptr NimStringV2](dest)
frees(s[])
zeroMem(dest, mt.size)
else:
unsureAsgnRef(cast[PPointer](dest), nil)

View File

@@ -92,14 +92,9 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
case mt.kind
of tyString:
when defined(nimSeqsV2):
when defined(nimsso):
var x = cast[ptr SmallString](dest)
var s2 = cast[ptr SmallString](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
var x = cast[ptr NimStringV2](dest)
var s2 = cast[ptr NimStringV2](s)[]
nimAsgnStrV2(x[], s2)
else:
var x = cast[PPointer](dest)
var s2 = cast[PPointer](s)[]

View File

@@ -30,8 +30,7 @@ proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline, systemRai
system.`[]`(s, s.len - int(i))
proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} =
a[Idx(a.len - int(i) + int low(a))]
when not defined(nimsso):
proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline, systemRaisesDefect.} = s[s.len - int(i)]
proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline, systemRaisesDefect.} = s[s.len - int(i)]
proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} =
system.`[]=`(s, s.len - int(i), x)

View File

@@ -735,7 +735,7 @@ proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.c
if s[i+1] == 'A' or s[i+1] == 'a':
if s[i+2] == 'N' or s[i+2] == 'n':
if s[i+3] notin IdentChars:
number = if sign: -NaN else: NaN
number = NaN
return i+3
return 0
if s[i] == 'I' or s[i] == 'i':

View File

@@ -272,16 +272,6 @@ proc newSeq[T](s: var seq[T], len: Natural) =
proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
proc nimCopySeqPayload(dest: pointer, src: pointer, elemSize: int, elemAlign: int) {.compilerRtl, inl.} =
## Bulk-copies the payload data from src seq to dest seq using copyMem.
## Only valid for trivially copyable element types (no GC refs, no destructors).
## Caller must have already ensured dest has the correct length and capacity
## (e.g. via setLen).
let d = cast[ptr NimRawSeq](dest)
let s = cast[ptr NimRawSeq](src)
if s.len > 0:
let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
copyMem(d.p +! headerSize, s.p +! headerSize, s.len * elemSize)
func capacity*[T](self: seq[T]): int {.inline.} =
## Returns the current capacity of the seq.

View File

@@ -10,46 +10,45 @@
# Compilerprocs for strings that do not depend on the string implementation.
import std/private/digitsutils as digitsutils2
when not defined(nimsso):
proc cmpStrings(a, b: string): int {.inline, compilerproc.} =
let alen = a.len
let blen = b.len
let minlen = min(alen, blen)
if minlen > 0:
result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int
if result == 0:
result = alen - blen
else:
proc cmpStrings(a, b: string): int {.inline, compilerproc.} =
let alen = a.len
let blen = b.len
let minlen = min(alen, blen)
if minlen > 0:
result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int
if result == 0:
result = alen - blen
else:
result = alen - blen
proc leStrings(a, b: string): bool {.inline, compilerproc.} =
# required by upcoming backends (NIR).
cmpStrings(a, b) <= 0
proc leStrings(a, b: string): bool {.inline, compilerproc.} =
# required by upcoming backends (NIR).
cmpStrings(a, b) <= 0
proc ltStrings(a, b: string): bool {.inline, compilerproc.} =
# required by upcoming backends (NIR).
cmpStrings(a, b) < 0
proc ltStrings(a, b: string): bool {.inline, compilerproc.} =
# required by upcoming backends (NIR).
cmpStrings(a, b) < 0
proc eqStrings(a, b: string): bool {.inline, compilerproc.} =
result = false
let alen = a.len
let blen = b.len
if alen == blen:
if alen == 0: return true
return equalMem(unsafeAddr(a[0]), unsafeAddr(b[0]), alen)
proc eqStrings(a, b: string): bool {.inline, compilerproc.} =
result = false
let alen = a.len
let blen = b.len
if alen == blen:
if alen == 0: return true
return equalMem(unsafeAddr(a[0]), unsafeAddr(b[0]), alen)
proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements
var h = 0'u
for i in 0..len(s)-1:
h = h + uint(s[i])
h = h + h shl 10
h = h xor (h shr 6)
h = h + h shl 3
h = h xor (h shr 11)
h = h + h shl 15
result = cast[int](h)
proc hashString(s: string): int {.compilerproc.} =
# the compiler needs exactly the same hash function!
# this used to be used for efficient generation of string case statements
var h = 0'u
for i in 0..len(s)-1:
h = h + uint(s[i])
h = h + h shl 10
h = h xor (h shr 6)
h = h + h shl 3
h = h xor (h shr 11)
h = h + h shl 15
result = cast[int](h)
proc eqCstrings(a, b: cstring): bool {.inline, compilerproc.} =
if pointer(a) == pointer(b): result = true
@@ -118,7 +117,7 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat,
if s[i+1] == 'A' or s[i+1] == 'a':
if s[i+2] == 'N' or s[i+2] == 'n':
if i+3 >= s.len or s[i+3] notin IdentChars:
number = if sign < 0: -NaN else: NaN
number = NaN
return i+3
return 0

View File

@@ -176,18 +176,18 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
a.len = b.len
copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
proc nimPrepareStrMutationImpl(s: var NimStringV2) {.raises: [], tags: [].} =
proc nimPrepareStrMutationImpl(s: var NimStringV2) =
let oldP = s.p
# can't mutate a literal, so we need a fresh copy here:
s.p = allocPayload(s.len)
s.p.cap = s.len
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inl, raises: [], tags: [].} =
proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inl.} =
if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag:
nimPrepareStrMutationImpl(s)
proc prepareMutation*(s: var string) {.inline, raises: [], tags: [].} =
proc prepareMutation*(s: var string) {.inline.} =
# string literals are "copy on write", so you need to call
# `prepareMutation` before modifying the strings via `addr`.
{.cast(noSideEffect).}:
@@ -216,25 +216,4 @@ 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
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
## Returns a writable pointer for bulk write of `ensuredLen` bytes starting at `start`.
## Call `endStore(s)` afterwards for portability.
{.cast(noSideEffect).}: prepareMutation(s)
let str = cast[ptr NimStringV2](unsafeAddr s)
if str.p == nil: nil
else: cast[ptr UncheckedArray[char]](addr str.p.data[start])
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
## No-op for non-SSO strings; call after bulk writes via `beginStore`.
discard
proc rawDataImpl(str: ptr NimStringV2; start: int): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
if str.p == nil: nil
else: cast[ptr UncheckedArray[char]](addr str.p.data[start])
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] =
## Returns a pointer to `s[start]` for read-only raw access.
## Template ensures no copy of `s`; ptr is valid while `s` is alive.
rawDataImpl(cast[ptr NimStringV2](unsafeAddr s), start)
{.pop.}

View File

@@ -1,743 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2026 Nim contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Small String Optimization (SSO) implementation used by Nim's core.
const
AlwaysAvail = sizeof(uint) - 1 # inline chars that fit in the `bytes` field alongside slen
PayloadSize = AlwaysAvail + sizeof(pointer) - 1 # -1 reserves the last byte for '\0'
HeapSlen = 255 # slen sentinel: heap-allocated long string; capImpl = raw capacity
StaticSlen = 254 # slen sentinel: static/literal long string; capImpl = 0, never freed
LongStringDataOffset = 3 * sizeof(int) # byte offset of LongString.data from struct start
when false:
proc atomicAddFetch(p: var int; v: int): int {.importc: "__sync_add_and_fetch", nodecl.}
proc atomicSubFetch(p: var int; v: int): int {.importc: "__sync_sub_and_fetch", nodecl.}
else:
proc atomicAddFetch(p: var int; v: int): int {.inline.} =
result = p + v
p = result
proc atomicSubFetch(p: var int; v: int): int {.inline.} =
result = p - v
p = result
type
LongString {.core.} = object
fullLen: int
rc: int # atomic reference count; 1 = unique owner
capImpl: int # raw capacity; 0 for static literals (never freed, slen = StaticSlen)
data: UncheckedArray[char]
SmallString {.core.} = object
bytes: uint
## Layout (little-endian): byte 0 = slen; bytes 1..AlwaysAvail = inline chars 0..AlwaysAvail-1.
## Bytes after the null terminator are zero (SWAR invariant).
## When slen == HeapSlen (255), `more` is a heap-owned LongString block.
## When slen == StaticSlen (254), `more` points to a static LongString literal.
## When AlwaysAvail < slen <= PayloadSize, `more` holds raw char bytes AlwaysAvail..PayloadSize-1 (medium string).
more: ptr LongString
when sizeof(uint) == 8:
proc bswap(x: uint): uint {.importc: "__builtin_bswap64", nodecl, noSideEffect.}
proc ctzImpl(x: uint): int {.inline.} =
proc ctz64(x: uint64): int32 {.importc: "__builtin_ctzll", nodecl, noSideEffect.}
int(ctz64(uint64(x)))
else:
proc bswap(x: uint): uint {.importc: "__builtin_bswap32", nodecl, noSideEffect.}
proc ctzImpl(x: uint): int {.inline.} =
proc ctz32(x: uint32): int32 {.importc: "__builtin_ctz", nodecl, noSideEffect.}
int(ctz32(uint32(x)))
proc swarKey(x: uint): uint {.inline.} =
## Returns a value where inline char[0] is in the most significant byte,
## so that integer comparison gives lexicographic string order.
## LE: slen in bits 0-7; `bswap(x shr 8)` puts char[0] in MSB.
## BE: slen in bits (sizeof(uint)-1)*8..(sizeof(uint)*8-1) (MSB); `x shl 8` shifts slen out, char[0] lands in MSB.
when system.cpuEndian == littleEndian:
bswap(x shr 8)
else:
x shl 8
# ---- accessors ----
# Memory layout is identical on both endiannesses: byte 0 = slen, bytes 1..AlwaysAvail = inline chars.
# But the integer value of `bytes` differs: on LE slen is in the LSB, on BE in the MSB.
template ssLenOf(bytes: uint): int =
## Extract slen from an already-loaded `bytes` word. Zero-cost (register op only).
## Use when `bytes` is already in a register (e.g. loaded for SWAR comparison).
when system.cpuEndian == littleEndian:
int(bytes and 0xFF'u)
else:
int(bytes shr (8 * (sizeof(uint) - 1)))
proc cmpShortInline(abytes, bbytes: uint; aslen, bslen: int): int {.inline.} =
let minLen = min(aslen, bslen)
if minLen > 0:
when system.cpuEndian == littleEndian:
let diffMask = (1'u shl (minLen * 8)) - 1'u
let diff = ((abytes xor bbytes) shr 8) and diffMask
if diff != 0:
let byteShift = (ctzImpl(diff) shr 3) * 8 + 8
let ac = (abytes shr byteShift) and 0xFF'u
let bc = (bbytes shr byteShift) and 0xFF'u
if ac < bc: return -1
return 1
else:
let aw = swarKey(abytes)
let bw = swarKey(bbytes)
if aw < bw: return -1
if aw > bw: return 1
aslen - bslen
template ssLen(s: SmallString): int =
## Load slen via a direct byte access at offset 0 (valid on both LE and BE).
## A byte load (movzx) lets the C compiler prove that slen is at offset 0,
## distinct from inline char writes at offsets 1+, enabling register-caching
## of slen across char-write loops (e.g. nimAddCharV1).
int(cast[ptr byte](unsafeAddr s.bytes)[])
template setSSLen(s: var SmallString; v: int) =
# Single byte store — equivalent to old `s.slen = byte(v)`.
# Accessing a uint via byte* is legal in C (char-pointer aliasing exemption).
cast[ptr byte](addr s.bytes)[] = cast[byte](v)
# Pointer to inline chars (offset +1 from `bytes` field / start of struct).
# Only valid when s is in memory (var/ptr); forces a load from memory.
template inlinePtr(s: SmallString): ptr UncheckedArray[char] =
cast[ptr UncheckedArray[char]](cast[uint](unsafeAddr s.bytes) + 1'u)
# Same but from a ptr SmallString (avoids unsafeAddr dance).
template inlinePtrOf(p: ptr SmallString): ptr UncheckedArray[char] =
cast[ptr UncheckedArray[char]](cast[uint](p) + 1'u)
proc resize(old: int): int {.inline.} =
## Capacity growth factor shared with seqs_v2.nim.
if old <= 0: result = 4
elif old <= high(int16): result = old * 2
else: result = old div 2 + old
# No Nim lifecycle hooks: the compiler calls the compilerRtl procs directly
# for tyString variables (nimDestroyStrV1, nimAsgnStrV2).
proc nimDestroyStrV1(s: SmallString) {.compilerRtl, inline.} =
if ssLen(s) == HeapSlen:
if atomicSubFetch(s.more.rc, 1) == 0:
dealloc(s.more)
proc ensureUniqueLong(s: var SmallString; oldLen, newLen: int) =
# Ensure s.more is a unique (rc=1) heap block with capacity >= newLen, preserving existing data.
# s must already be a long string (slen >= StaticSlen) on entry.
# After return, slen == HeapSlen (s is heap-owned).
let isHeap = ssLen(s) == HeapSlen
let cap = if isHeap: s.more.capImpl else: 0 # static literals have capImpl=0
if isHeap and s.more.rc == 1 and newLen <= cap:
s.more.fullLen = newLen
else:
# Only grow capacity when actually needed; pure COW copies (newLen <= cap)
# preserve the existing capacity to avoid exponential growth via repeated COW.
let newCap = if newLen > cap: max(newLen, resize(cap)) else: cap
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
p.rc = 1
p.fullLen = newLen
p.capImpl = newCap
let old = s.more
copyMem(addr p.data[0], addr old.data[0], oldLen + 1) # +1 preserves the '\0'
if isHeap and atomicSubFetch(old.rc, 1) == 0:
dealloc(old)
s.more = p
setSSLen(s, HeapSlen) # mark as heap-owned (also handles static→heap promotion)
proc len(s: SmallString): int {.inline.} =
result = ssLen(s)
if result > PayloadSize:
result = s.more.fullLen
template guts(s: SmallString): (int, ptr UncheckedArray[char]) =
let slen = ssLen(s)
if slen > PayloadSize:
(s.more.fullLen, cast[ptr UncheckedArray[char]](addr s.more.data[0]))
else:
(slen, inlinePtr(s))
proc nimStrAtV3*(s: var SmallString; i: int): char {.compilerproc, inline.} =
if ssLen(s) <= PayloadSize:
# short/medium: data is in the inline bytes overlay
result = inlinePtr(s)[i]
else:
# long: always use heap data (completeStore keeps more.data canonical)
result = s.more.data[i]
proc nimStrPutV3*(s: var SmallString; i: int; c: char) {.compilerproc, inline.} =
let slen = ssLen(s)
if slen <= PayloadSize:
# unchecked: when i >= 7 we store into the `more` overlay
inlinePtr(s)[i] = c
# Maintain SWAR zeroing invariant: if i < AlwaysAvail and we wrote a non-null,
# caller is responsible. Writing '\0' here would break content. No action needed.
else:
let l = s.more.fullLen
ensureUniqueLong(s, l, l) # COW if shared; length unchanged
s.more.data[i] = c
if i < AlwaysAvail:
inlinePtr(s)[i] = c
proc cmpInlineBytes(a, b: ptr UncheckedArray[char]; n: int): int {.inline.} =
for i in 0..<n:
let ac = a[i]
let bc = b[i]
if ac < bc: return -1
if ac > bc: return 1
proc cmpStringPtrs(a, b: ptr SmallString): int {.inline.} =
# Compare two SmallStrings by pointer to avoid struct copies in the hot path.
let abytes = a.bytes
let bbytes = b.bytes
let aslen = ssLenOf(abytes)
let bslen = ssLenOf(bbytes)
if aslen <= AlwaysAvail and bslen <= AlwaysAvail:
# SWAR path: both short (≤7 bytes). All data lives in the `bytes` field.
# Zeroed-padding invariant ensures bytes past the null are 0.
# swarKey puts char[0] in the MSB → integer comparison is lexicographic.
let aw = swarKey(abytes)
let bw = swarKey(bbytes)
if aw < bw: return -1
if aw > bw: return 1
return aslen - bslen
if aslen <= PayloadSize and bslen <= PayloadSize:
# Both inline/medium: all data lives in the flat struct, no heap access needed.
let minLen = min(aslen, bslen)
let pfxLen = min(minLen, AlwaysAvail)
result = cmpInlineBytes(inlinePtrOf(a), inlinePtrOf(b), pfxLen)
if result != 0: return
if minLen > AlwaysAvail:
let aInl = inlinePtrOf(a)
let bInl = inlinePtrOf(b)
result = cmpInlineBytes(
cast[ptr UncheckedArray[char]](addr aInl[AlwaysAvail]),
cast[ptr UncheckedArray[char]](addr bInl[AlwaysAvail]),
minLen - AlwaysAvail)
if result == 0: result = aslen - bslen
return
# At least one is long. Hot prefix: inlinePtr[0..AlwaysAvail-1] mirrors heap data.
let pfxLen = min(min(aslen, bslen), AlwaysAvail)
result = cmpInlineBytes(inlinePtrOf(a), inlinePtrOf(b), pfxLen)
if result != 0: return
let la = if aslen > PayloadSize: a.more.fullLen else: aslen
let lb = if bslen > PayloadSize: b.more.fullLen else: bslen
let minLen = min(la, lb)
if minLen <= AlwaysAvail:
result = la - lb
return
let ap = if aslen > PayloadSize: cast[ptr UncheckedArray[char]](addr a.more.data[0]) else:
inlinePtrOf(a)
let bp = if bslen > PayloadSize: cast[ptr UncheckedArray[char]](addr b.more.data[0]) else:
inlinePtrOf(b)
result = cmpMem(addr ap[AlwaysAvail], addr bp[AlwaysAvail], minLen - AlwaysAvail)
if result == 0: result = la - lb
proc cmp(a, b: SmallString): int {.inline.} =
# Load bytes once per string — used for both slen check and SWAR key.
let abytes = a.bytes
let bbytes = b.bytes
let aslen = ssLenOf(abytes)
let bslen = ssLenOf(bbytes)
if aslen <= AlwaysAvail and bslen <= AlwaysAvail:
return cmpShortInline(abytes, bbytes, aslen, bslen)
cmpStringPtrs(unsafeAddr a, unsafeAddr b)
proc `==`(a, b: SmallString): bool {.inline.} =
let abytes = a.bytes
let bbytes = b.bytes
let aslen = ssLenOf(abytes)
let bslen = ssLenOf(bbytes)
if aslen <= AlwaysAvail and bslen <= AlwaysAvail:
return abytes == bbytes # SWAR: slen equal, data in bytes word
# Compute actual lengths (sentinels 254/255 → more.fullLen)
let la = if aslen > PayloadSize: a.more.fullLen else: aslen
let lb = if bslen > PayloadSize: b.more.fullLen else: bslen
if la != lb: return false
if la == 0: return true
if aslen <= PayloadSize and bslen <= PayloadSize:
# Both medium (slen == la == lb, so byte0 equal): compare prefix word + tail
if abytes != bbytes: return false
let (_, pa) = a.guts
let (_, pb) = b.guts
return cmpMem(addr pa[AlwaysAvail], addr pb[AlwaysAvail], la - AlwaysAvail) == 0
# At least one long (heap or static): delegate to cmpStringPtrs
cmpStringPtrs(unsafeAddr a, unsafeAddr b) == 0
proc continuesWith*(s, sub: SmallString; start: int): bool =
if start < 0: return false
let subslen = ssLen(sub)
if subslen == 0: return true
let sslen = ssLen(s)
# Compare via hot prefix first where possible (no heap dereference).
let pfxLen = min(subslen, max(0, AlwaysAvail - start))
if pfxLen > 0:
if cmpMem(cast[pointer](cast[uint](unsafeAddr s.bytes) + 1'u + uint(start)),
cast[pointer](cast[uint](unsafeAddr sub.bytes) + 1'u), pfxLen) != 0:
return false
# Fetch actual lengths and compare the remaining tail via heap/guts.
let subLen = if subslen > PayloadSize: sub.more.fullLen else: subslen
let sLen = if sslen > PayloadSize: s.more.fullLen else: sslen
if start + subLen > sLen: return false
if pfxLen == subLen: return true
let (_, sp) = s.guts
let (_, subp) = sub.guts
cmpMem(addr sp[start + pfxLen], addr subp[pfxLen], subLen - pfxLen) == 0
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) =
let slen = ssLen(s)
if slen <= PayloadSize:
let newLen = slen + 1
if newLen <= PayloadSize:
let inl = inlinePtr(s)
inl[slen] = c
inl[newLen] = '\0'
setSSLen(s, newLen)
else:
# transition from medium (slen == PayloadSize) to long
let cap = newLen * 2
let p = cast[ptr LongString](alloc(LongStringDataOffset + cap + 1))
p.rc = 1
p.fullLen = newLen
p.capImpl = cap
copyMem(addr p.data[0], inlinePtr(s), slen)
p.data[slen] = c
p.data[newLen] = '\0'
s.more = p
setSSLen(s, HeapSlen)
else:
let l = s.more.fullLen # fetch fullLen only in the long path
ensureUniqueLong(s, l, l + 1)
s.more.data[l] = c
s.more.data[l + 1] = '\0'
if l < AlwaysAvail:
inlinePtr(s)[l] = c
proc add(s: var SmallString; t: SmallString) =
let slen = ssLen(s)
let (tl, tp) = t.guts # fetch t's guts before any mutation (aliasing safety)
if tl == 0: return
if slen <= PayloadSize:
let sl = slen # for short/medium, slen IS the actual length
let newLen = sl + tl
if newLen <= PayloadSize:
let inl = inlinePtr(s)
copyMem(addr inl[sl], tp, tl)
inl[newLen] = '\0'
setSSLen(s, newLen)
else:
# transition to long
let cap = newLen * 2
let p = cast[ptr LongString](alloc(LongStringDataOffset + cap + 1))
p.rc = 1
p.fullLen = newLen
p.capImpl = cap
copyMem(addr p.data[0], inlinePtr(s), sl)
copyMem(addr p.data[sl], tp, tl)
p.data[newLen] = '\0'
if sl < AlwaysAvail:
copyMem(addr inlinePtr(s)[sl], tp, min(AlwaysAvail - sl, tl))
s.more = p
setSSLen(s, HeapSlen)
else:
let sl = s.more.fullLen # fetch fullLen only in the long path
let newLen = sl + tl
# tp was read before ensureUniqueLong: if t.more == s.more, rc decrements but won't hit 0
ensureUniqueLong(s, sl, newLen)
copyMem(addr s.more.data[sl], tp, tl)
s.more.data[newLen] = '\0'
if sl < AlwaysAvail:
copyMem(addr inlinePtr(s)[sl], tp, min(AlwaysAvail - sl, tl))
{.push overflowChecks: off, rangeChecks: off.}
proc prepareAddLong(s: var SmallString; newLen: int) =
# Reserve capacity for newLen in the long-string block without changing logical length.
let isHeap = ssLen(s) == HeapSlen
let cap = if isHeap: s.more.capImpl else: 0
if isHeap and s.more.rc == 1 and newLen <= cap:
discard # already unique with sufficient capacity
else:
let oldLen = s.more.fullLen
let newCap = max(newLen, resize(cap))
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
p.rc = 1
p.fullLen = oldLen # logical length unchanged — caller sets it after writing data
p.capImpl = newCap
let old = s.more
copyMem(addr p.data[0], addr old.data[0], oldLen + 1)
if isHeap and atomicSubFetch(old.rc, 1) == 0:
dealloc(old)
s.more = p
setSSLen(s, HeapSlen)
proc prepareAdd(s: var SmallString; addLen: int) {.compilerRtl.} =
## Ensure s has room for addLen more characters without changing its length.
let slen = ssLen(s)
let curLen = if slen > PayloadSize: s.more.fullLen else: slen
let newLen = curLen + addLen
if slen <= PayloadSize:
if newLen > PayloadSize:
# transition to long: allocate, copy existing data
let newCap = newLen * 2
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
p.rc = 1
p.fullLen = curLen
p.capImpl = newCap
copyMem(addr p.data[0], inlinePtr(s), curLen + 1)
s.more = p
setSSLen(s, HeapSlen)
# else: short/medium — inline capacity always sufficient (struct is fixed size)
else:
prepareAddLong(s, newLen)
proc nimAddCharV1(s: var SmallString; c: char) {.compilerRtl, inline.} =
let slen = ssLen(s)
if slen < PayloadSize:
# Hot path: inline/medium with room (slen+1 <= PayloadSize, no heap needed)
let inl = inlinePtr(s)
inl[slen] = c
inl[slen + 1] = '\0'
setSSLen(s, slen + 1)
elif slen > PayloadSize:
# Long string — inline the common case: unique heap block with room
let l = s.more.fullLen
if slen == HeapSlen and s.more.rc == 1 and l < s.more.capImpl:
s.more.data[l] = c
s.more.data[l + 1] = '\0'
s.more.fullLen = l + 1
if l < AlwaysAvail:
inlinePtr(s)[l] = c
else:
prepareAdd(s, 1)
s.add(c)
else:
# slen == PayloadSize: medium→long transition (rare)
prepareAdd(s, 1)
s.add(c)
proc toNimStr(str: cstring; len: int): SmallString {.compilerproc.} =
if len <= 0: return
if len <= PayloadSize:
setSSLen(result, len)
let inl = inlinePtr(result)
copyMem(inl, str, len)
inl[len] = '\0'
# Bytes past inl[len] in `bytes` must be zero for SWAR. `result` is zero-initialized,
# and copyMem only fills bytes 0..len-1 of inl; bytes len..6 remain zero.
else:
let p = cast[ptr LongString](alloc(LongStringDataOffset + len + 1))
p.rc = 1
p.fullLen = len
p.capImpl = len
copyMem(addr p.data[0], str, len)
p.data[len] = '\0'
copyMem(inlinePtr(result), str, AlwaysAvail)
setSSLen(result, HeapSlen)
result.more = p
proc cstrToNimstr(str: cstring): SmallString {.compilerRtl.} =
if str == nil: return
toNimStr(str, str.len)
proc nimToCStringConv(s: var SmallString): cstring {.compilerproc, nonReloadable, inline.} =
## Returns a null-terminated C string pointer into s's data.
## Takes by var (pointer) so the inline chars ptr is always valid.
if ssLen(s) > PayloadSize:
cast[cstring](addr s.more.data[0])
else:
cast[cstring](inlinePtr(s))
proc appendString(dest: var SmallString; src: SmallString) {.compilerproc, inline.} =
dest.add(src)
proc appendChar(dest: var SmallString; c: char) {.compilerproc, inline.} =
dest.add(c)
proc rawNewString(space: int): SmallString {.compilerproc.} =
## Returns an empty SmallString with capacity reserved for `space` chars (newStringOfCap).
if space <= 0: return
if space <= PayloadSize:
discard # inline capacity is always available; nothing to pre-allocate
else:
let p = cast[ptr LongString](alloc(LongStringDataOffset + space + 1))
p.rc = 1
p.fullLen = 0
p.capImpl = space
p.data[0] = '\0'
result.more = p
setSSLen(result, HeapSlen)
proc mnewString(len: int): SmallString {.compilerproc.} =
## Returns a SmallString of `len` zero characters (newString).
if len <= 0: return
if len <= PayloadSize:
setSSLen(result, len)
# bytes field is zero-initialized (result starts at 0); inline chars are already 0.
# Null terminator at inlinePtr(result)[len] is also 0 — fine for SWAR invariant.
else:
let p = cast[ptr LongString](alloc0(LongStringDataOffset + len + 1))
p.rc = 1
p.fullLen = len
p.capImpl = len
# data is zeroed by alloc0; data[len] is '\0' too
result.more = p
setSSLen(result, HeapSlen)
proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
## Sets the length of s to newLen, zeroing new bytes on growth.
let slen = ssLen(s)
let curLen = if slen > PayloadSize: s.more.fullLen else: slen
if newLen == curLen: return
if newLen <= 0:
if slen > PayloadSize:
if slen == HeapSlen and s.more.rc == 1:
s.more.fullLen = 0
s.more.data[0] = '\0'
else:
# shared or static block: detach and go back to empty inline
nimDestroyStrV1(s)
s.bytes = 0 # slen=0, all inline chars zeroed
else:
s.bytes = 0 # slen=0, all inline chars zeroed (SWAR safe)
return
if slen <= PayloadSize:
if newLen <= PayloadSize:
let inl = inlinePtr(s)
if newLen > curLen:
zeroMem(addr inl[curLen], newLen - curLen)
inl[newLen] = '\0'
setSSLen(s, newLen)
else:
# Shrink: zero out padding bytes for SWAR invariant.
inl[newLen] = '\0'
if newLen < AlwaysAvail:
# Zero bytes newLen+1..AlwaysAvail-1 in `bytes` (chars newLen..AlwaysAvail-2
# are now padding and must be 0 for SWAR comparison to work correctly).
when system.cpuEndian == littleEndian:
# LE: slen in bits 0-7; keep bits 0..(newLen+1)*8-1, clear the rest above.
let keepBits = (newLen + 1) * 8
let charMask = ((uint(1) shl keepBits) - 1'u) and not 0xFF'u
s.bytes = (s.bytes and charMask) or uint(newLen)
else:
# BE: slen in the top byte; keep top (newLen+1) bytes, zero the rest below.
let discardBits = (AlwaysAvail - newLen) * 8
let slenBit = 8 * (sizeof(uint) - 1)
let charMask = not ((uint(1) shl discardBits) - 1'u) and not (0xFF'u shl slenBit)
s.bytes = (s.bytes and charMask) or (uint(newLen) shl slenBit)
else:
setSSLen(s, newLen)
else:
# grow into long
let newCap = resize(newLen)
let p = cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
p.rc = 1
p.fullLen = newLen
p.capImpl = newCap
copyMem(addr p.data[0], inlinePtr(s), curLen)
# bytes [curLen..newLen] zeroed by alloc0; p.data[newLen] = '\0' by alloc0
s.more = p
setSSLen(s, HeapSlen)
else:
# currently long
if newLen <= PayloadSize:
# shrink back to inline
let old = s.more
let inl = inlinePtr(s)
copyMem(inl, addr old.data[0], newLen)
inl[newLen] = '\0'
if slen == HeapSlen and atomicSubFetch(old.rc, 1) == 0:
dealloc(old)
# Zero padding bytes in `bytes` for SWAR invariant
if newLen < AlwaysAvail:
when system.cpuEndian == littleEndian:
let keepBits = (newLen + 1) * 8
let charMask = ((uint(1) shl keepBits) - 1'u) and not 0xFF'u
s.bytes = (s.bytes and charMask) or uint(newLen)
else:
let discardBits = (AlwaysAvail - newLen) * 8
let slenBit = 8 * (sizeof(uint) - 1)
let charMask = not ((uint(1) shl discardBits) - 1'u) and not (0xFF'u shl slenBit)
s.bytes = (s.bytes and charMask) or (uint(newLen) shl slenBit)
else:
setSSLen(s, newLen)
else:
ensureUniqueLong(s, curLen, newLen)
if newLen > curLen:
zeroMem(addr s.more.data[curLen], newLen - curLen)
s.more.data[newLen] = '\0'
s.more.fullLen = newLen
proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl, inline.} =
if ssLen(b) <= PayloadSize:
nimDestroyStrV1(a) # free any existing heap block before overwriting
copyMem(addr a, unsafeAddr b, sizeof(SmallString))
else:
if addr(a) == unsafeAddr(b): return
nimDestroyStrV1(a)
# COW: share the block, bump refcount — no allocation needed (static literals: no bump)
if ssLenOf(b.bytes) == HeapSlen:
discard atomicAddFetch(b.more.rc, 1)
copyMem(addr a, unsafeAddr b, sizeof(SmallString))
proc nimPrepareStrMutationImpl(s: var SmallString) =
# Called when s holds a static (slen=StaticSlen) LongString block. COW: allocate fresh copy.
let old = s.more
let oldLen = old.fullLen
let p = cast[ptr LongString](alloc(LongStringDataOffset + oldLen + 1))
p.rc = 1
p.fullLen = oldLen
p.capImpl = oldLen
copyMem(addr p.data[0], addr old.data[0], oldLen + 1)
s.more = p
setSSLen(s, HeapSlen) # promote from static to heap-owned
proc nimPrepareStrMutationV2(s: var SmallString) {.compilerRtl, inline.} =
if ssLen(s) == StaticSlen:
nimPrepareStrMutationImpl(s)
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 ssLen(s) > PayloadSize:
nimPrepareStrMutationV2(s) # COW: ensure unique heap block before exposing ref
result = s.more.data[i]
else:
result = inlinePtr(s)[i]
proc nimAddStrV1(s: var SmallString; src: SmallString) {.compilerRtl, inline.} =
s.add(src)
func capacity*(self: SmallString): int {.inline.} =
## Returns the current capacity of the string.
let slen = ssLen(self)
if slen == HeapSlen:
self.more.capImpl
elif slen == StaticSlen:
self.more.fullLen # static: report fullLen as capacity (read-only, no extra room)
else:
PayloadSize
proc nimStrLen(s: SmallString): int {.compilerproc, inline.} =
## Returns the length of s. Called by the codegen for `mLen` on strings with -d:nimsso.
s.len
proc nimStrData(s: var SmallString): ptr UncheckedArray[char] {.compilerproc, inline.} =
## Returns a pointer to the char data of s. Called by codegen for subscript and slice with -d:nimsso.
if ssLen(s) > PayloadSize: cast[ptr UncheckedArray[char]](addr s.more.data[0])
else: inlinePtr(s)
const
newStringUninitWasDeclared = true
proc newStringUninitImpl(len: Natural): string {.noSideEffect, inline.} =
## Returns a new string of length `len` but with uninitialized content.
## One needs to fill the string character after character
## with the index operator `s[i]`.
##
## This procedure exists only for optimization purposes;
## the same effect can be achieved with the `&` operator or with `add`.
when nimvm:
result = newString(len)
else:
result = newStringOfCap(len) # rawNewString: alloc (not alloc0) for long strings
{.cast(noSideEffect).}:
if len > 0:
let s = cast[ptr SmallString](addr result)
if len <= PayloadSize:
setSSLen(s[], len)
# Null-terminate; bytes [0..len-1] left uninitialized for caller to fill.
inlinePtr(s[])[len] = '\0'
else:
# rawNewString allocated with alloc (not alloc0), so data[0..len-1] is
# intentionally uninitialized. Caller fills it and calls completeStore.
s.more.fullLen = len
s.more.data[len] = '\0'
proc completeStore(s: var SmallString) {.compilerproc, inline.} =
## Must be called after bulk data has been written directly into the string buffer
## via a raw pointer obtained from `nimStrData`/`nimStrAtMutV3` (e.g. `readBuffer`,
## `moveMem`, `copyMem`).
##
## Syncs the hot prefix cache: copies `more.data[0..AlwaysAvail-1]` into
## the inline bytes so that `cmp`/`==` can compare long strings
## without a heap dereference for the first few bytes.
if ssLen(s) > PayloadSize:
copyMem(inlinePtr(s), addr s.more.data[0], AlwaysAvail)
proc completeStore*(s: var string) {.inline.} =
completeStore(cast[ptr SmallString](addr s)[])
proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
## Prepares `s` for a bulk write of `ensuredLen` bytes starting at `start`.
## The caller must ensure `s.len >= start + ensuredLen` (e.g. via `newString` or `setLen`).
## Call `endStore(s)` afterwards to sync the inline cache.
{.cast(noSideEffect).}:
let ss = cast[ptr SmallString](addr s)
let slen = ssLen(ss[])
if slen > PayloadSize:
ensureUniqueLong(ss[], ss[].more.fullLen, ss[].more.fullLen)
result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
else:
result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start))
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
## Syncs the inline cache after bulk writes via `beginStore`. No-op for short/medium strings.
{.cast(noSideEffect).}: completeStore(cast[ptr SmallString](addr s)[])
proc rawDataImpl(ss: ptr SmallString; start: int): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [].} =
let slen = ssLen(ss[])
let actualLen = if slen > PayloadSize: ss[].more.fullLen else: slen
if actualLen == 0: nil
elif slen > PayloadSize: cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
else: cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start))
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] =
## Returns a pointer to `s[start]` for read-only raw access.
## Template ensures no copy of `s` is made; ptr is valid while `s` is alive.
rawDataImpl(cast[ptr SmallString](unsafeAddr s), start)
# These take `string` (tyString) so the codegen uses them directly, bypassing
# strmantle.nim's versions which go through nimStrLen/nimStrAtMutV3 compilerproc calls.
proc cmpStrings(a, b: string): int {.compilerproc, inline.} =
cmpStringPtrs(cast[ptr SmallString](unsafeAddr a), cast[ptr SmallString](unsafeAddr b))
proc eqStrings(a, b: string): bool {.compilerproc, inline.} =
cast[ptr SmallString](unsafeAddr a)[] == cast[ptr SmallString](unsafeAddr b)[]
proc leStrings(a, b: string): bool {.compilerproc, inline.} =
cmpStrings(a, b) <= 0
proc ltStrings(a, b: string): bool {.compilerproc, inline.} =
cmpStrings(a, b) < 0
proc hashString(s: string): int {.compilerproc.} =
let ss = cast[ptr SmallString](unsafeAddr s)[]
let (L, data) = ss.guts
var h = 0'u
for i in 0..<L:
h = h + uint(data[i])
h = h + h shl 10
h = h xor (h shr 6)
h = h + h shl 3
h = h xor (h shr 11)
h = h + h shl 15
result = cast[int](h)
{.pop.}

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">nimdoc/extlinks/util</h1>
<div class="row">

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">Nothing User Manual</h1>

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">nimdoc/extlinks/project/main</h1>
<div class="row">
@@ -102,7 +99,7 @@
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="A">
<dt><pre><a href="#A"><span class="Identifier">A</span></a> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
<dt><pre><a href="main.html#A"><span class="Identifier">A</span></a> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
<dd>

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">nimdoc/extlinks/project/sub/submodule</h1>
<div class="row">
@@ -91,7 +88,7 @@
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="submoduleInt">
<dt><pre><a href="#submoduleInt"><span class="Identifier">submoduleInt</span></a> <span class="Other">=</span> <span class="Keyword">distinct</span> <span class="Identifier">int</span></pre></dt>
<dt><pre><a href="submodule.html#submoduleInt"><span class="Identifier">submoduleInt</span></a> <span class="Other">=</span> <span class="Keyword">distinct</span> <span class="Identifier">int</span></pre></dt>
<dd>

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">Index</h1>
Documents: <a href="doc/manual.html">Nothing User Manual</a>.<br/><p />Modules: <a href="_._/util.html">../util</a>, <a href="main.html">main</a>, <a href="sub/submodule.html">sub/submodule</a>.<br/><p /><h2>API symbols</h2>

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">Not a Nim Manual</h1>
<div class="row">

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">nimdoc/test_doctype/test_doctype</h1>
<div class="row">

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">nimdoc/test_out_index_dot_html/foo</h1>
<div class="row">

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">Index</h1>
Modules: <a href="index.html">index</a>.<br/><p /><h2>API symbols</h2>

View File

@@ -123,6 +123,7 @@ Modified by Boyd Greenfield and narimiran
}
html {
overflow-x: hidden;
max-width: 100%;
box-sizing: border-box;
font-size: 100%;
@@ -155,8 +156,7 @@ body {
margin-left: 1%; }
@media print {
#global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby,
#nav-burger, #nav-overlay, .three.columns {
#global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {
display:none;
}
.columns {
@@ -175,7 +175,6 @@ body {
height: 100vh;
position: sticky;
top: 0px;
left: 0px;
overflow-y: auto;
padding: 2px;
}
@@ -189,67 +188,9 @@ body {
width: 100%;
margin-left: 0; }
#nav-burger, #nav-overlay {
display: none;
}
@media screen and (max-width: 860px) {
#nav-burger {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0.25em;
left: 0.25em;
z-index: 200;
width: 1.6rem;
height: 1.6rem;
font-size: 1.25em;
cursor: pointer;
border-radius: 4px;
background-color: var(--secondary-background);
color: var(--text);
border: 1px solid var(--border);
user-select: none;
opacity: 0.55;
}
#nav-burger:hover {
background-color: var(--third-background);
}
#nav-toggle:checked ~ .container .three.columns {
transform: translateX(0);
}
#nav-toggle:checked ~ #nav-overlay {
opacity: 1;
pointer-events: auto;
}
#nav-overlay {
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 99; /* below sidebar */
background: rgba(0, 0, 0, 0.35);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease;
}
.three.columns {
display: block;
position: fixed;
left: 0;
width: min(80vw, 24em);
padding-top: 1.6em;
height: 100vh; /* Fallback */
height: 100dvh;
overflow-y: auto;
z-index: 100;
background-color: var(--secondary-background);
box-shadow: 2px 0 12px rgba(0,0,0,0.25);
transform: translateX(-110%);
transition: transform 0.25s ease;
display: none;
}
.nine.columns {
width: 100%;
@@ -259,8 +200,6 @@ body {
body {
font-size: 1em;
line-height: 1.35;
margin-left: 0.35em;
margin-right: 0.35em;
}
}
@@ -419,10 +358,6 @@ img {
h1.title {
page-break-before: avoid; }
.nine.columns h1:first-of-type {
page-break-before: avoid;
}
p, h2, h3 {
orphans: 3;
@@ -490,22 +425,6 @@ h5 {
h6 {
font-size: 1.1em; }
@media screen and (max-width: 860px) {
h1.title {
font-size: 2em;
}
h1 {
font-size: 1.5em;
margin-top: 1.5em;
margin-bottom: 0.75em;
}
h2 {
margin-top: 1.3em;
}
h3 {
margin-top: 1.2em;
}
}
ul, ol {
padding: 0;
@@ -653,8 +572,8 @@ blockquote.markdown-quote {
padding-left: 3px;
padding-right: 3px;
border-radius: 4px;
white-space: pre-wrap;
overflow-wrap: break-word;
white-space: normal;
word-break: break-all;
}
span.tok {
@@ -689,15 +608,6 @@ pre {
border-radius: 6px;
}
@media screen and (max-width: 860px) {
pre {
font-stretch: semi-condensed;
letter-spacing: -0.25px;
line-height: 1.25;
padding: 0.33em;
}
}
.copyToClipBoardBtn {
visibility: hidden;
position: absolute;
@@ -764,8 +674,6 @@ table {
border-collapse: collapse;
border-color: var(--third-background);
border-spacing: 0;
display: block;
overflow-x: auto;
}
table:not(.line-nums-table) {

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">subdir/subdir_b/utils</h1>
<div class="row">
@@ -260,7 +257,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="G">
<dt><pre><a href="#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
<dt><pre><a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
<dd>
@@ -268,7 +265,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</dd>
</div>
<div id="SomeType">
<dt><pre><a href="#SomeType"><span class="Identifier">SomeType</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<dt><pre><a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">enumValueA</span><span class="Other">,</span> <span class="Identifier">enumValueB</span><span class="Other">,</span> <span class="Identifier">enumValueC</span></pre></dt>
<dd>
@@ -284,7 +281,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
<dl class="item">
<div id="$-procs-all">
<div id="$,G[T]">
<dt><pre><span class="Keyword">proc</span> <a href="#%24%2CG%5BT%5D"><span class="Identifier">`$`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <a href="#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#%24%2CG%5BT%5D"><span class="Identifier">`$`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
@@ -292,7 +289,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</dd>
</div>
<div id="$,ref.SomeType">
<dt><pre><span class="Keyword">proc</span> <a href="#%24%2Cref.SomeType"><span class="Identifier">`$`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Keyword">ref</span> <a href="#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#%24%2Cref.SomeType"><span class="Identifier">`$`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Keyword">ref</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
@@ -303,7 +300,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</div>
<div id="'big-procs-all">
<div id="'big,string">
<dt><pre><span class="Keyword">func</span> <a href="#%27big%2Cstring"><span class="Identifier">`'big`</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <a href="#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">func</span> <a href="#%27big%2Cstring"><span class="Identifier">`'big`</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
@@ -314,7 +311,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</div>
<div id="[]-procs-all">
<div id="[],G[T]">
<dt><pre><span class="Keyword">proc</span> <a href="#%5B%5D%2CG%5BT%5D"><span class="Identifier">`[]`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#%5B%5D%2CG%5BT%5D"><span class="Identifier">`[]`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
<dd>
@@ -325,7 +322,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</div>
<div id="[]=-procs-all">
<div id="[]=,G[T],int,T">
<dt><pre><span class="Keyword">proc</span> <a href="#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT"><span class="Identifier">`[]=`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Keyword">var</span> <a href="#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">index</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span></pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT"><span class="Identifier">`[]=`</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Keyword">var</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">;</span> <span class="Identifier">index</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">value</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span></pre></dt>
<dd>
@@ -348,7 +345,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</div>
<div id="f-procs-all">
<div id="f,G[int]">
<dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bint%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bint%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
There is also variant <a class="reference internal nimdoc" title="proc f(x: G[string])" href="#f,G[string]">f(G[string])</a>
@@ -356,7 +353,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</dd>
</div>
<div id="f,G[string]">
<dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bstring%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bstring%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
See also <a class="reference internal nimdoc" title="proc f(x: G[int])" href="#f,G[int]">f(G[int])</a>.
@@ -531,7 +528,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
</div>
<div id="someType-procs-all">
<div id="someType_2">
<dt><pre><span class="Keyword">proc</span> <a href="#someType_2"><span class="Identifier">someType</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#someType_2"><span class="Identifier">someType</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
constructor.
@@ -548,7 +545,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
<dl class="item">
<div id="fooBar-iterators-all">
<div id="fooBar.i,seq[SomeType]">
<dt><pre><span class="Keyword">iterator</span> <a href="#fooBar.i%2Cseq%5BSomeType%5D"><span class="Identifier">fooBar</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><a href="#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">iterator</span> <a href="#fooBar.i%2Cseq%5BSomeType%5D"><span class="Identifier">fooBar</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">testproject</h1>
<div class="row">
@@ -384,7 +381,7 @@
<h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item">
<div id="A">
<dt><pre><a href="#A"><span class="Identifier">A</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
<dt><pre><a href="testproject.html#A"><span class="Identifier">A</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">aA</span></pre></dt>
<dd>
@@ -393,7 +390,7 @@
</dd>
</div>
<div id="AnotherObject">
<dt><pre><a href="#AnotherObject"><span class="Identifier">AnotherObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<dt><pre><a href="testproject.html#AnotherObject"><span class="Identifier">AnotherObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Keyword">case</span> <span class="Identifier">x</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span>
<span class="Keyword">of</span> <span class="Identifier">true</span><span class="Other">:</span>
<span class="Identifier">y</span><span class="Operator">*</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span>
@@ -405,7 +402,7 @@
</dd>
</div>
<div id="B">
<dt><pre><a href="#B"><span class="Identifier">B</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
<dt><pre><a href="testproject.html#B"><span class="Identifier">B</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">bB</span></pre></dt>
<dd>
@@ -414,7 +411,7 @@
</dd>
</div>
<div id="Foo">
<dt><pre><a href="#Foo"><span class="Identifier">Foo</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<dt><pre><a href="testproject.html#Foo"><span class="Identifier">Foo</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">enumValueA2</span></pre></dt>
<dd>
@@ -423,7 +420,7 @@
</dd>
</div>
<div id="FooBuzz">
<dt><pre><a href="#FooBuzz"><span class="Identifier">FooBuzz</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">deprecated</span><span class="Other">:</span> <span class="StringLit">&quot;FooBuzz msg&quot;</span></span>.} <span class="Other">=</span> <span class="Identifier">int</span></pre></dt>
<dt><pre><a href="testproject.html#FooBuzz"><span class="Identifier">FooBuzz</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">deprecated</span><span class="Other">:</span> <span class="StringLit">&quot;FooBuzz msg&quot;</span></span>.} <span class="Other">=</span> <span class="Identifier">int</span></pre></dt>
<dd>
<div class="deprecation-message">
<b>Deprecated:</b> FooBuzz msg
@@ -434,7 +431,7 @@
</dd>
</div>
<div id="MyObject">
<dt><pre><a href="#MyObject"><span class="Identifier">MyObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<dt><pre><a href="testproject.html#MyObject"><span class="Identifier">MyObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Identifier">someString</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is a string</span>
<span class="Identifier">annotated</span><span class="Operator">*</span> {.<span class="Identifier">somePragma</span>.}<span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is an annotated string</span></pre></dt>
<dd>
@@ -444,7 +441,7 @@
</dd>
</div>
<div id="Shapes">
<dt><pre><a href="#Shapes"><span class="Identifier">Shapes</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<dt><pre><a href="testproject.html#Shapes"><span class="Identifier">Shapes</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">Circle</span><span class="Other">,</span> <span class="Comment">## A circle</span>
<span class="Identifier">Triangle</span><span class="Other">,</span> <span class="Comment">## A three-sided shape</span>
<span class="Identifier">Rectangle</span> <span class="Comment">## A four-sided shape</span></pre></dt>
@@ -455,7 +452,7 @@
</dd>
</div>
<div id="T19396">
<dt><pre><a href="#T19396"><span class="Identifier">T19396</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<dt><pre><a href="testproject.html#T19396"><span class="Identifier">T19396</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Identifier">a</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt>
<dd>
@@ -464,7 +461,7 @@
</dd>
</div>
<div id="Xxx">
<dt><pre><a href="#Xxx"><span class="Identifier">Xxx</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<dt><pre><a href="testproject.html#Xxx"><span class="Identifier">Xxx</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
<span class="Identifier">field</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span>
<span class="Identifier">field3</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span> <span class="Comment">## Doc comment2</span></pre></dt>
<dd>
@@ -480,7 +477,7 @@
<h1><a class="toc-backref" href="#8">Vars</a></h1>
<dl class="item">
<div id="aVariable">
<dt><pre><a href="#aVariable"><span class="Identifier">aVariable</span></a><span class="Other">:</span> <span class="Identifier">array</span><span class="Other">[</span><span class="DecNumber">1</span><span class="Other">,</span> <span class="Identifier">int</span><span class="Other">]</span></pre></dt>
<dt><pre><a href="testproject.html#aVariable"><span class="Identifier">aVariable</span></a><span class="Other">:</span> <span class="Identifier">array</span><span class="Other">[</span><span class="DecNumber">1</span><span class="Other">,</span> <span class="Identifier">int</span><span class="Other">]</span></pre></dt>
<dd>
@@ -488,7 +485,7 @@
</dd>
</div>
<div id="someVariable">
<dt><pre><a href="#someVariable"><span class="Identifier">someVariable</span></a><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
<dt><pre><a href="testproject.html#someVariable"><span class="Identifier">someVariable</span></a><span class="Other">:</span> <span class="Identifier">bool</span></pre></dt>
<dd>
This should be visible.
@@ -502,7 +499,7 @@
<h1><a class="toc-backref" href="#10">Consts</a></h1>
<dl class="item">
<div id="C_A">
<dt><pre><a href="#C_A"><span class="Identifier">C_A</span></a> <span class="Other">=</span> <span class="FloatNumber">0x7FF0000000000000'f64</span></pre></dt>
<dt><pre><a href="testproject.html#C_A"><span class="Identifier">C_A</span></a> <span class="Other">=</span> <span class="FloatNumber">0x7FF0000000000000'f64</span></pre></dt>
<dd>
@@ -510,7 +507,7 @@
</dd>
</div>
<div id="C_B">
<dt><pre><a href="#C_B"><span class="Identifier">C_B</span></a> <span class="Other">=</span> <span class="DecNumber">0o377'i8</span></pre></dt>
<dt><pre><a href="testproject.html#C_B"><span class="Identifier">C_B</span></a> <span class="Other">=</span> <span class="DecNumber">0o377'i8</span></pre></dt>
<dd>
@@ -518,7 +515,7 @@
</dd>
</div>
<div id="C_C">
<dt><pre><a href="#C_C"><span class="Identifier">C_C</span></a> <span class="Other">=</span> <span class="DecNumber">0o277'i8</span></pre></dt>
<dt><pre><a href="testproject.html#C_C"><span class="Identifier">C_C</span></a> <span class="Other">=</span> <span class="DecNumber">0o277'i8</span></pre></dt>
<dd>
@@ -526,7 +523,7 @@
</dd>
</div>
<div id="C_D">
<dt><pre><a href="#C_D"><span class="Identifier">C_D</span></a> <span class="Other">=</span> <span class="DecNumber">0o177777'i16</span></pre></dt>
<dt><pre><a href="testproject.html#C_D"><span class="Identifier">C_D</span></a> <span class="Other">=</span> <span class="DecNumber">0o177777'i16</span></pre></dt>
<dd>
@@ -614,7 +611,7 @@
</div>
<div id="bar-procs-all">
<div id="bar">
<dt><pre><span class="Keyword">proc</span> <a href="#bar"><span class="Identifier">bar</span></a><span class="Other">(</span><span class="Identifier">f</span><span class="Other">:</span> <a href="#FooBuzz"><span class="Identifier">FooBuzz</span></a><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#bar"><span class="Identifier">bar</span></a><span class="Other">(</span><span class="Identifier">f</span><span class="Other">:</span> <a href="testproject.html#FooBuzz"><span class="Identifier">FooBuzz</span></a><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
@@ -838,7 +835,7 @@ at indent 0
</div>
<div id="z1-procs-all">
<div id="z1">
<dt><pre><span class="Keyword">proc</span> <a href="#z1"><span class="Identifier">z1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="#Foo"><span class="Identifier">Foo</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dt><pre><span class="Keyword">proc</span> <a href="#z1"><span class="Identifier">z1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="testproject.html#Foo"><span class="Identifier">Foo</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
<dd>
cz1

View File

@@ -23,9 +23,6 @@
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">Index</h1>
Modules: <a href="subdir/subdir_b/utils.html">subdir/subdir_b/utils</a>, <a href="testproject.html">testproject</a>.<br/><p /><h2>API symbols</h2>

View File

@@ -1018,7 +1018,7 @@ proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: Su
if n.kind == nkSym and n.sym.checkSymbol(n.info):
graph.suggestResult(n.sym, n.sym.info, ideOutline, endInfo.line, endInfo.col)
return true
elif n.kind in {nkIdent, nkAccQuoted}:
elif n.kind == nkIdent:
let symData = findByTLineInfo(n.info, infoPairs)
if symData != nil and symData.sym.checkSymbol(symData.info):
let sym = symData.sym
@@ -1028,7 +1028,7 @@ proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: Su
proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
result = false
for child in n:
if child.kind in {nkIdent, nkAccQuoted, nkSym}:
if child.kind in {nkIdent, nkSym}:
if graph.outlineNode(child, endInfo, infoPairs):
return true
elif child.kind == nkPostfix:

View File

@@ -365,28 +365,3 @@ proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] =
do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b)))
doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]"""
block:
type RangeCrash = object
case x: range[0..7]
of 0..2:
a: string
else:
b: string
var rangeCrash = RangeCrash()
{.cast(uncheckedAssign).}:
rangeCrash.x = 5
block:
type Discrim = distinct uint8
type DistinctCrash = object
case x: Discrim
of Discrim(0)..Discrim(2):
a: string
else:
b: string
var distinctCrash = DistinctCrash()
{.cast(uncheckedAssign).}:
distinctCrash.x = Discrim(5)

View File

@@ -1,261 +0,0 @@
import std/[monotimes, os, random, strutils, times]
const
AlwaysAvail = 7
InlineMax = AlwaysAvail + sizeof(pointer) - 1
Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
SharedPrefixes = [
"module/submodule/symbol/",
"compiler/semantic/checker/",
"core/runtime/string-table/",
"aaaaaaaaaaaaaa/shared/prefix/",
"zzzzzzzzzzzzzz/shared/prefix/"
]
ScenarioNames = ["short", "inline", "boundary", "long", "prefix", "mixed"]
type
Scenario = enum
scShort
scInline
scBoundary
scLong
scPrefix
scMixed
Pair = tuple[a, b: string]
Config = object
count: int
rounds: int
seed: int64
scenarios: seq[Scenario]
proc defaultConfig(): Config =
Config(
count: 400_000,
rounds: 8,
seed: 20260307'i64,
scenarios: @[scShort, scInline, scBoundary, scLong, scMixed]
)
proc usage() =
echo "String comparison benchmark for experimenting with the SSO runtime."
echo ""
echo "Usage:"
echo " nim r -d:danger cmpbench.nim [--count=N] [--rounds=N] [--seed=N]"
echo " [--scenarios=list]"
echo ""
echo "Scenarios:"
echo " short, inline, boundary, long, prefix, mixed"
echo ""
echo "Current inline limit on this target: ", InlineMax, " bytes"
proc parseScenario(name: string): Scenario =
case name.normalize
of "short":
scShort
of "inline":
scInline
of "boundary":
scBoundary
of "long":
scLong
of "prefix":
scPrefix
of "mixed":
scMixed
else:
quit "unknown scenario: " & name
proc parseConfig(): Config =
result = defaultConfig()
for arg in commandLineParams():
if arg == "--help" or arg == "-h":
usage()
quit 0
elif arg.startsWith("--count="):
result.count = parseInt(arg["--count=".len .. ^1])
elif arg.startsWith("--rounds="):
result.rounds = parseInt(arg["--rounds=".len .. ^1])
elif arg.startsWith("--seed="):
result.seed = parseInt(arg["--seed=".len .. ^1]).int64
elif arg.startsWith("--scenarios="):
result.scenarios.setLen(0)
for item in arg["--scenarios=".len .. ^1].split(','):
if item.len > 0:
result.scenarios.add parseScenario(item)
else:
quit "unknown argument: " & arg
if result.count <= 0:
quit "--count must be > 0"
if result.rounds <= 0:
quit "--rounds must be > 0"
if result.scenarios.len == 0:
quit "at least one scenario is required"
proc scenarioName(s: Scenario): string =
ScenarioNames[s.ord]
proc scenarioList(scenarios: openArray[Scenario]): string =
for i, scenario in scenarios:
if i > 0:
result.add ','
result.add scenarioName(scenario)
proc fixed(x: float; digits: range[0..32]): string =
formatFloat(x, ffDecimal, digits)
proc randomChar(rng: var Rand): char =
Alphabet[rng.rand(Alphabet.high)]
proc makeRandomString(rng: var Rand; len: int; prefix = ""): string =
result = newString(len)
var i = 0
while i < len and i < prefix.len:
result[i] = prefix[i]
inc i
while i < len:
result[i] = randomChar(rng)
inc i
proc pickMixedLength(rng: var Rand): int =
let bucket = rng.rand(0..99)
if bucket < 35:
result = rng.rand(1..AlwaysAvail)
elif bucket < 70:
result = rng.rand(AlwaysAvail + 1 .. InlineMax)
else:
result = rng.rand(InlineMax + 1 .. InlineMax + 48)
proc makeScenarioString(rng: var Rand; kind: Scenario; serial: int): string =
case kind
of scShort:
result = makeRandomString(rng, rng.rand(1..AlwaysAvail))
of scInline:
result = makeRandomString(rng, rng.rand(AlwaysAvail + 1 .. InlineMax))
of scBoundary:
let choices = [
max(1, InlineMax - 2),
max(1, InlineMax - 1),
InlineMax,
InlineMax + 1,
InlineMax + 2
]
result = makeRandomString(rng, choices[rng.rand(choices.high)])
of scLong:
result = makeRandomString(rng, rng.rand(InlineMax + 1 .. InlineMax + 64))
of scPrefix:
let prefix = SharedPrefixes[rng.rand(SharedPrefixes.high)]
let suffixLen = rng.rand(4..24)
result = makeRandomString(rng, prefix.len + suffixLen, prefix)
of scMixed:
result = makeRandomString(rng, pickMixedLength(rng))
if kind == scPrefix and result.len > 0:
# Keep the shared-prefix workload adversarial on purpose.
result[^1] = char(ord('0') + (serial mod 10))
proc generateDataset(kind: Scenario; count: int; seed: int64): seq[string] =
var rng = initRand(seed + kind.ord.int64 * 10_000_019'i64)
result = newSeq[string](count)
for i in 0..<count:
result[i] = makeScenarioString(rng, kind, i)
proc tweakTail(s: string; salt: int): string =
result = s
if result.len == 0:
result = "x"
elif result.len == 1:
result[0] = char(ord('a') + (salt mod 26))
else:
result[^1] = char(ord('a') + (salt mod 26))
proc buildPairs(kind: Scenario; data: openArray[string]): seq[Pair] =
result = newSeq[Pair](data.len)
let n = max(1, data.len)
for i in 0..<data.len:
let a = data[i]
let j = (i * 48271 + 17) mod n
let k = (i * 69621 + 91) mod n
if kind == scPrefix:
case i mod 4
of 0:
result[i] = (a, data[j])
of 1:
result[i] = (a, a)
of 2:
result[i] = (a, tweakTail(a, i))
else:
result[i] = (a, data[(i + 1) mod n])
else:
# Default workload: mostly unrelated words, with a small minority of harder cases.
case i mod 10
of 0:
result[i] = (a, a)
of 1:
result[i] = (a, tweakTail(a, i))
of 2:
result[i] = (a, data[(i + 1) mod n])
else:
result[i] = (a, data[if j == i: k else: j])
proc averageLen(data: openArray[string]): float =
var total = 0
for s in data:
total += s.len
result = total.float / max(1, data.len).float
proc pairChecksum(pairs: openArray[Pair]): uint64 =
for i, pair in pairs:
result = result * 0x9E3779B185EBCA87'u64 + uint64(pair.a.len + pair.b.len)
if pair.a.len > 0:
result = result xor (uint64(ord(pair.a[0])) shl (i and 7))
if pair.b.len > 0:
result = result xor (uint64(ord(pair.b[^1])) shl ((i + 3) and 7))
proc bench(kind: Scenario; cfg: Config) =
let data = generateDataset(kind, cfg.count, cfg.seed)
let pairs = buildPairs(kind, data)
let avgLen = averageLen(data)
var warm = 0
for pair in pairs:
warm += system.cmp(pair.a, pair.b)
var totalNs = 0.0
var bestNs = Inf
var worstNs = 0.0
var combined = uint64(cast[uint](warm)) xor pairChecksum(pairs)
for round in 0..<cfg.rounds:
var acc = 0
let started = getMonoTime()
for pair in pairs:
acc += system.cmp(pair.a, pair.b)
let elapsedNs = float((getMonoTime() - started).inNanoseconds)
totalNs += elapsedNs
bestNs = min(bestNs, elapsedNs)
worstNs = max(worstNs, elapsedNs)
combined = combined * 0x9E3779B185EBCA87'u64 + uint64(cast[uint](acc)) + uint64(round + 1)
let avgNs = totalNs / cfg.rounds.float
let nsPerCmp = avgNs / pairs.len.float
echo align(scenarioName(kind), 8), " n=", align($pairs.len, 8),
" avgLen=", align(fixed(avgLen, 1), 6),
" avg=", align(fixed(avgNs / 1e6, 3), 9), " ms",
" best=", align(fixed(bestNs / 1e6, 3), 9), " ms",
" worst=", align(fixed(worstNs / 1e6, 3), 9), " ms",
" ns/cmp=", align(fixed(nsPerCmp, 1), 8),
" check=0x", toHex(combined, 16)
proc main() =
let cfg = parseConfig()
echo "inline limit=", InlineMax, " bytes count=", cfg.count,
" rounds=", cfg.rounds, " seed=", cfg.seed
echo "scenarios=", scenarioList(cfg.scenarios)
for scenario in cfg.scenarios:
bench(scenario, cfg)
when not defined(useMalloc): echo "MAXMEM=", formatSize getMaxMem()
when isMainModule:
main()

View File

@@ -1,171 +0,0 @@
import std/[monotimes, os, parsecsv, random, strutils, times]
const
FirstNames = [
"amy", "ben", "chris", "dora", "ella", "finn", "gina", "hugo",
"ivan", "june", "kyle", "lena", "mona", "nina", "owen", "paul"
]
LastNames = [
"li", "ng", "kim", "ross", "miles", "stone", "young", "ward",
"reed", "clark", "hall", "price", "woods", "perry", "cohen", "moore"
]
type
StoredRow = object
id: string
name: string
age: string
score: string
visits: string
zip: string
timestamp: string
url: string
Config = object
rows: int
rounds: int
seed: int64
proc defaultConfig(): Config =
Config(rows: 100_000, rounds: 4, seed: 20260307'i64)
proc usage() =
echo "CSV parse/materialize benchmark for experimenting with the SSO runtime."
echo ""
echo "Usage:"
echo " nim r -d:danger csvbench.nim [--rows=N] [--rounds=N] [--seed=N]"
proc parseConfig(): Config =
result = defaultConfig()
for arg in commandLineParams():
if arg == "--help" or arg == "-h":
usage()
quit 0
elif arg.startsWith("--rows="):
result.rows = parseInt(arg["--rows=".len .. ^1])
elif arg.startsWith("--rounds="):
result.rounds = parseInt(arg["--rounds=".len .. ^1])
elif arg.startsWith("--seed="):
result.seed = parseInt(arg["--seed=".len .. ^1]).int64
else:
quit "unknown argument: " & arg
if result.rows <= 0:
quit "--rows must be > 0"
if result.rounds <= 0:
quit "--rounds must be > 0"
proc fixed(x: float; digits: range[0..32]): string =
formatFloat(x, ffDecimal, digits)
proc makeName(rng: var Rand; serial: int): string =
result = FirstNames[rng.rand(FirstNames.high)] & "_" &
LastNames[(serial + rng.rand(LastNames.high)) mod LastNames.len]
proc makeUrl(name: string; serial: int; score: int): string =
"https://data.example/api/u/" & name & "/" & $serial &
"?score=" & $score & "&src=csv"
proc csvPath(cfg: Config): string =
getTempDir() / ("nim_csvbench_" & $cfg.rows & "_" & $cfg.seed & ".csv")
proc writeCsv(path: string; cfg: Config) =
var rng = initRand(cfg.seed)
var f = open(path, fmWrite)
defer: close(f)
f.writeLine("id,name,age,score,visits,zip,timestamp,url")
for i in 0..<cfg.rows:
let name = makeName(rng, i)
let age = 18 + (i mod 63)
let score = 1000 + rng.rand(0..900_000)
let visits = rng.rand(0..20_000)
let zip = 10000 + rng.rand(0..89999)
let ts = 1700000000'i64 + i.int64 * 17 + rng.rand(0..999).int64
let url = makeUrl(name, i, score)
f.write($i)
f.write(',')
f.write(name)
f.write(',')
f.write($age)
f.write(',')
f.write($score)
f.write(',')
f.write($visits)
f.write(',')
f.write($zip)
f.write(',')
f.write($ts)
f.write(',')
f.writeLine(url)
proc checksum(row: StoredRow): uint64 =
let fields = [
row.id, row.name, row.age, row.score,
row.visits, row.zip, row.timestamp, row.url
]
for i, field in fields:
result = result * 0x9E3779B185EBCA87'u64 + uint64(field.len + i)
if field.len > 0:
result = result xor (uint64(ord(field[0])) shl (i and 7))
result = result xor (uint64(ord(field[^1])) shl ((i + 3) and 7))
proc parseAndMaterialize(path: string; rowsExpected: int): tuple[elapsedNs: float, check: uint64] =
var parser: CsvParser
parser.open(path)
defer: parser.close()
parser.readHeaderRow()
var rows = newSeqOfCap[StoredRow](rowsExpected)
let started = getMonoTime()
while parser.readRow():
var row: StoredRow
row.id = parser.row[0]
row.name = parser.row[1]
row.age = parser.row[2]
row.score = parser.row[3]
row.visits = parser.row[4]
row.zip = parser.row[5]
row.timestamp = parser.row[6]
row.url = parser.row[7]
result.check = result.check * 0x9E3779B185EBCA87'u64 + checksum(row)
rows.add row
result.elapsedNs = float((getMonoTime() - started).inNanoseconds)
doAssert rows.len == rowsExpected
proc main() =
let cfg = parseConfig()
let path = csvPath(cfg)
writeCsv(path, cfg)
defer:
if fileExists(path):
removeFile(path)
let fileSize = getFileSize(path)
var warm = parseAndMaterialize(path, cfg.rows)
discard warm
var totalNs = 0.0
var bestNs = Inf
var worstNs = 0.0
var combined = uint64(fileSize) + uint64(cfg.rows)
for round in 0..<cfg.rounds:
let run = parseAndMaterialize(path, cfg.rows)
totalNs += run.elapsedNs
bestNs = min(bestNs, run.elapsedNs)
worstNs = max(worstNs, run.elapsedNs)
combined = combined * 0x9E3779B185EBCA87'u64 + run.check + uint64(round + 1)
let avgNs = totalNs / cfg.rounds.float
let nsPerRow = avgNs / cfg.rows.float
echo "rows=", cfg.rows, " rounds=", cfg.rounds, " seed=", cfg.seed,
" file=", formatSize(fileSize)
echo "avg=", fixed(avgNs / 1e6, 3), " ms",
" best=", fixed(bestNs / 1e6, 3), " ms",
" worst=", fixed(worstNs / 1e6, 3), " ms",
" ns/row=", fixed(nsPerRow, 1),
" check=0x", toHex(combined, 16)
when not defined(useMalloc): echo "MAXMEM=", formatSize getMaxMem()
when isMainModule:
main()

View File

@@ -1,277 +0,0 @@
import std/[monotimes, os, random, strutils, tables, times]
const
AlwaysAvail = 7
InlineMax = AlwaysAvail + sizeof(pointer) - 1
Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
SharedPrefixes = [
"module/submodule/symbol/",
"compiler/semantic/checker/",
"core/runtime/string-table/",
"aaaaaaaaaaaaaa/shared/prefix/",
"zzzzzzzzzzzzzz/shared/prefix/"
]
ScenarioNames = ["short", "inline", "boundary", "long", "prefix", "mixed"]
type
Scenario = enum
scShort
scInline
scBoundary
scLong
scPrefix
scMixed
Config = object
count: int
rounds: int
seed: int64
scenarios: seq[Scenario]
proc defaultConfig(): Config =
Config(
count: 200_000,
rounds: 5,
seed: 20260307'i64,
scenarios: @[scShort, scInline, scBoundary, scLong, scPrefix, scMixed]
)
proc usage() =
echo "String hash-table benchmark for experimenting with the SSO runtime."
echo ""
echo "Usage:"
echo " nim r -d:danger hashbench.nim [--count=N] [--rounds=N] [--seed=N]"
echo " [--scenarios=list]"
echo ""
echo "Scenarios:"
echo " short, inline, boundary, long, prefix, mixed"
echo ""
echo "Current inline limit on this target: ", InlineMax, " bytes"
proc parseScenario(name: string): Scenario =
case name.normalize
of "short":
scShort
of "inline":
scInline
of "boundary":
scBoundary
of "long":
scLong
of "prefix":
scPrefix
of "mixed":
scMixed
else:
quit "unknown scenario: " & name
proc parseConfig(): Config =
result = defaultConfig()
for arg in commandLineParams():
if arg == "--help" or arg == "-h":
usage()
quit 0
elif arg.startsWith("--count="):
result.count = parseInt(arg["--count=".len .. ^1])
elif arg.startsWith("--rounds="):
result.rounds = parseInt(arg["--rounds=".len .. ^1])
elif arg.startsWith("--seed="):
result.seed = parseInt(arg["--seed=".len .. ^1]).int64
elif arg.startsWith("--scenarios="):
result.scenarios.setLen(0)
for item in arg["--scenarios=".len .. ^1].split(','):
if item.len > 0:
result.scenarios.add parseScenario(item)
else:
quit "unknown argument: " & arg
if result.count <= 0:
quit "--count must be > 0"
if result.rounds <= 0:
quit "--rounds must be > 0"
if result.scenarios.len == 0:
quit "at least one scenario is required"
proc scenarioName(s: Scenario): string =
ScenarioNames[s.ord]
proc scenarioList(scenarios: openArray[Scenario]): string =
for i, scenario in scenarios:
if i > 0:
result.add ','
result.add scenarioName(scenario)
proc fixed(x: float; digits: range[0..32]): string =
formatFloat(x, ffDecimal, digits)
proc randomChar(rng: var Rand): char =
Alphabet[rng.rand(Alphabet.high)]
proc makeRandomString(rng: var Rand; len: int; prefix = ""): string =
result = newString(len)
var i = 0
while i < len and i < prefix.len:
result[i] = prefix[i]
inc i
while i < len:
result[i] = randomChar(rng)
inc i
proc pickMixedLength(rng: var Rand): int =
let bucket = rng.rand(0..99)
if bucket < 35:
result = rng.rand(1..AlwaysAvail)
elif bucket < 70:
result = rng.rand(AlwaysAvail + 1 .. InlineMax)
else:
result = rng.rand(InlineMax + 1 .. InlineMax + 48)
proc makeScenarioString(rng: var Rand; kind: Scenario; serial: int): string =
case kind
of scShort:
result = makeRandomString(rng, rng.rand(1..AlwaysAvail))
of scInline:
result = makeRandomString(rng, rng.rand(AlwaysAvail + 1 .. InlineMax))
of scBoundary:
let choices = [
max(1, InlineMax - 2),
max(1, InlineMax - 1),
InlineMax,
InlineMax + 1,
InlineMax + 2
]
result = makeRandomString(rng, choices[rng.rand(choices.high)])
of scLong:
result = makeRandomString(rng, rng.rand(InlineMax + 1 .. InlineMax + 64))
of scPrefix:
let prefix = SharedPrefixes[rng.rand(SharedPrefixes.high)]
let suffixLen = rng.rand(4..24)
result = makeRandomString(rng, prefix.len + suffixLen, prefix)
of scMixed:
result = makeRandomString(rng, pickMixedLength(rng))
if result.len > 0:
result[0] = char(ord('a') + (serial mod 26))
result[^1] = char(ord('0') + (serial mod 10))
proc generateDataset(kind: Scenario; count: int; seed: int64): seq[string] =
var rng = initRand(seed + kind.ord.int64 * 10_000_019'i64)
result = newSeq[string](count)
for i in 0..<count:
result[i] = makeScenarioString(rng, kind, i)
proc averageLen(data: openArray[string]): float =
var total = 0
for s in data:
total += s.len
result = total.float / max(1, data.len).float
proc checksum(data: openArray[string]): uint64 =
for i, s in data:
result = result * 0x9E3779B185EBCA87'u64 + uint64(s.len)
if s.len > 0:
result = result xor (uint64(ord(s[0])) shl (i and 7))
result = result xor (uint64(ord(s[^1])) shl ((i + 3) and 7))
proc makeMissQueries(kind: Scenario; count: int; seed: int64): seq[string] =
result = generateDataset(kind, count, seed + 0x6A09E667'i64)
for i in 0..<result.len:
if result[i].len == 0:
result[i] = "!"
else:
result[i][^1] = char(ord('Q') + (i mod 7))
proc bench(kind: Scenario; cfg: Config) =
let keys = generateDataset(kind, cfg.count, cfg.seed)
let hitQueries = keys
let missQueries = makeMissQueries(kind, cfg.count, cfg.seed)
let avgLen = averageLen(keys)
let keyCheck = checksum(keys) xor checksum(missQueries)
var warm = initTable[string, int](cfg.count * 2)
for i, key in keys:
warm[key] = i
var warmHits = 0
for key in hitQueries:
warmHits += warm[key]
var warmMisses = 0
for key in missQueries:
if warm.hasKey(key):
inc warmMisses
doAssert warmHits >= 0
doAssert warmMisses == 0
var insertTotalNs = 0.0
var hitTotalNs = 0.0
var missTotalNs = 0.0
var insertBestNs = Inf
var hitBestNs = Inf
var missBestNs = Inf
var insertWorstNs = 0.0
var hitWorstNs = 0.0
var missWorstNs = 0.0
var combined = keyCheck + uint64(cfg.count)
for round in 0..<cfg.rounds:
var table = initTable[string, int](cfg.count * 2)
let insertStarted = getMonoTime()
for i, key in keys:
table[key] = i
let insertNs = float((getMonoTime() - insertStarted).inNanoseconds)
var hitSum = 0
let hitStarted = getMonoTime()
for key in hitQueries:
hitSum += table[key]
let hitNs = float((getMonoTime() - hitStarted).inNanoseconds)
var missSum = 0
let missStarted = getMonoTime()
for key in missQueries:
if table.hasKey(key):
inc missSum
let missNs = float((getMonoTime() - missStarted).inNanoseconds)
doAssert hitSum >= 0
doAssert missSum == 0
insertTotalNs += insertNs
hitTotalNs += hitNs
missTotalNs += missNs
insertBestNs = min(insertBestNs, insertNs)
hitBestNs = min(hitBestNs, hitNs)
missBestNs = min(missBestNs, missNs)
insertWorstNs = max(insertWorstNs, insertNs)
hitWorstNs = max(hitWorstNs, hitNs)
missWorstNs = max(missWorstNs, missNs)
combined = combined * 0x9E3779B185EBCA87'u64 +
uint64(cast[uint](hitSum xor missSum xor round))
let insertAvgNs = insertTotalNs / cfg.rounds.float
let hitAvgNs = hitTotalNs / cfg.rounds.float
let missAvgNs = missTotalNs / cfg.rounds.float
echo align(scenarioName(kind), 8), " n=", align($cfg.count, 8),
" avgLen=", align(fixed(avgLen, 1), 6),
" ins=", align(fixed(insertAvgNs / 1e6, 3), 9), " ms",
" hit=", align(fixed(hitAvgNs / 1e6, 3), 9), " ms",
" miss=", align(fixed(missAvgNs / 1e6, 3), 9), " ms",
" ns/op=", align(fixed((insertAvgNs + hitAvgNs + missAvgNs) / (3.0 * cfg.count.float), 1), 8),
" check=0x", toHex(combined, 16)
discard insertBestNs
discard hitBestNs
discard missBestNs
discard insertWorstNs
discard hitWorstNs
discard missWorstNs
proc main() =
let cfg = parseConfig()
echo "inline limit=", InlineMax, " bytes count=", cfg.count,
" rounds=", cfg.rounds, " seed=", cfg.seed
echo "scenarios=", scenarioList(cfg.scenarios)
for scenario in cfg.scenarios:
bench(scenario, cfg)
when not defined(useMalloc): echo "MAXMEM=", formatSize getMaxMem()
when isMainModule:
main()

View File

@@ -1,224 +0,0 @@
import std/[algorithm, monotimes, os, random, strutils, times]
const
AlwaysAvail = 7
InlineMax = AlwaysAvail + sizeof(pointer) - 1
Alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
SharedPrefixes = [
"module/submodule/symbol/",
"compiler/semantic/checker/",
"core/runtime/string-table/",
"aaaaaaaaaaaaaa/shared/prefix/",
"zzzzzzzzzzzzzz/shared/prefix/"
]
ScenarioNames = ["short", "inline", "boundary", "long", "prefix", "mixed"]
type
Scenario = enum
scShort
scInline
scBoundary
scLong
scMixed
Config = object
count: int
rounds: int
seed: int64
scenarios: seq[Scenario]
proc defaultConfig(): Config =
Config(
count: 200_000,
rounds: 5,
seed: 20260307'i64,
scenarios: @[scShort, scInline, scBoundary, scLong, scMixed]
)
proc usage() =
echo "String sorting benchmark for experimenting with the SSO runtime."
echo ""
echo "Usage:"
echo " nim r -d:danger sortbench.nim [--count=N] [--rounds=N] [--seed=N]"
echo " [--scenarios=list]"
echo ""
echo "Scenarios:"
echo " short, inline, boundary, long, prefix, mixed"
echo ""
echo "Current inline limit on this target: ", InlineMax, " bytes"
proc parseScenario(name: string): Scenario =
case name.normalize
of "short":
scShort
of "inline":
scInline
of "boundary":
scBoundary
of "long":
scLong
of "mixed":
scMixed
else:
quit "unknown scenario: " & name
proc parseConfig(): Config =
result = defaultConfig()
for arg in commandLineParams():
if arg == "--help" or arg == "-h":
usage()
quit 0
elif arg.startsWith("--count="):
result.count = parseInt(arg["--count=".len .. ^1])
elif arg.startsWith("--rounds="):
result.rounds = parseInt(arg["--rounds=".len .. ^1])
elif arg.startsWith("--seed="):
result.seed = parseInt(arg["--seed=".len .. ^1]).int64
elif arg.startsWith("--scenarios="):
result.scenarios.setLen(0)
for item in arg["--scenarios=".len .. ^1].split(','):
if item.len > 0:
result.scenarios.add parseScenario(item)
else:
quit "unknown argument: " & arg
if result.count <= 0:
quit "--count must be > 0"
if result.rounds <= 0:
quit "--rounds must be > 0"
if result.scenarios.len == 0:
quit "at least one scenario is required"
proc scenarioName(s: Scenario): string =
ScenarioNames[s.ord]
proc randomChar(rng: var Rand): char =
Alphabet[rng.rand(Alphabet.high)]
proc makeRandomString(rng: var Rand; len: int): string =
result = newString(len)
var i = 0
while i < len:
result[i] = randomChar(rng)
inc i
proc pickMixedLength(rng: var Rand): int =
let bucket = rng.rand(0..99)
if bucket < 35:
result = rng.rand(1..AlwaysAvail)
elif bucket < 70:
result = rng.rand(AlwaysAvail + 1 .. InlineMax)
else:
result = rng.rand(InlineMax + 1 .. InlineMax + 48)
proc makeScenarioString(rng: var Rand; kind: Scenario; serial: int): string =
case kind
of scShort:
result = makeRandomString(rng, rng.rand(1..AlwaysAvail))
of scInline:
result = makeRandomString(rng, rng.rand(1 .. InlineMax))
of scBoundary:
let choices = [
max(1, InlineMax - 2),
max(1, InlineMax - 1),
InlineMax,
InlineMax + 1,
InlineMax + 2
]
result = makeRandomString(rng, choices[rng.rand(choices.high)])
of scLong:
result = makeRandomString(rng, rng.rand(InlineMax + 1 .. InlineMax + 64))
of scMixed:
result = makeRandomString(rng, pickMixedLength(rng))
# Inject a little deterministic structure so equal prefixes are common but not identical.
if result.len > 0:
result[0] = char(ord('a') + (serial mod 26))
result[^1] = char(ord('0') + (serial mod 10))
proc generateDataset(kind: Scenario; count: int; seed: int64): seq[string] =
var rng = initRand(seed + kind.ord.int64 * 10_000_019'i64)
result = newSeq[string](count)
for i in 0..<count:
result[i] = makeScenarioString(rng, kind, i)
proc cloneStrings(src: seq[string]): seq[string] =
result = newSeq[string](src.len)
for i, s in src:
result[i] = s
proc isSorted(a: openArray[string]): bool =
for i in 1..<a.len:
if cmp(a[i - 1], a[i]) > 0:
return false
result = true
proc checksum(a: openArray[string]): uint64 =
for i, s in a:
result = result * 0x9E3779B185EBCA87'u64 + uint64(s.len)
if s.len > 0:
result = result xor (uint64(ord(s[0])) shl (i and 7))
result = result xor (uint64(ord(s[^1])) shl ((i + 3) and 7))
proc averageLen(data: openArray[string]): float =
var total = 0
for s in data:
total += s.len
result = total.float / max(1, data.len).float
proc scenarioList(scenarios: openArray[Scenario]): string =
for i, scenario in scenarios:
if i > 0:
result.add ','
result.add scenarioName(scenario)
proc fixed(x: float; digits: range[0..32]): string =
formatFloat(x, ffDecimal, digits)
proc bench(kind: Scenario; cfg: Config) =
let data = generateDataset(kind, cfg.count, cfg.seed)
let avgLen = averageLen(data)
var warmup = cloneStrings(data)
warmup.sort(system.cmp)
doAssert isSorted(warmup)
var totalNs = 0.0
var bestNs = Inf
var worstNs = 0.0
var combinedChecksum = 0'u64
for round in 0..<cfg.rounds:
var working = cloneStrings(data)
let started = getMonoTime()
working.sort(system.cmp)
let elapsedNs = float((getMonoTime() - started).inNanoseconds)
doAssert isSorted(working)
totalNs += elapsedNs
bestNs = min(bestNs, elapsedNs)
worstNs = max(worstNs, elapsedNs)
combinedChecksum = combinedChecksum * 0x9E3779B185EBCA87'u64 +
checksum(working) + uint64(round + 1)
let avgNs = totalNs / cfg.rounds.float
let nsPerItem = avgNs / cfg.count.float
echo align(scenarioName(kind), 8), " n=", align($cfg.count, 8),
" avgLen=", align(fixed(avgLen, 1), 6),
" avg=", align(fixed(avgNs / 1e6, 3), 9), " ms",
" best=", align(fixed(bestNs / 1e6, 3), 9), " ms",
" worst=", align(fixed(worstNs / 1e6, 3), 9), " ms",
" ns/item=", align(fixed(nsPerItem, 1), 8),
" check=0x", toHex(combinedChecksum, 16)
proc main() =
let cfg = parseConfig()
echo "inline limit=", InlineMax, " bytes count=", cfg.count,
" rounds=", cfg.rounds, " seed=", cfg.seed
echo "scenarios=" & scenarioList(cfg.scenarios)
for scenario in cfg.scenarios:
bench(scenario, cfg)
when not defined(useMalloc): echo "MAXMEM=", formatSize getMaxMem()
when isMainModule:
main()

View File

@@ -130,14 +130,3 @@ block: # issue #22646
var x: Vec[3, float]
let y = Color(x)
doAssert Vec3[float](y) == x
block: # bug #25697
type MyList = distinct seq[int]
iterator items(x: MyList): lent int {.borrow.}
let s = MyList(@[1, 2, 3])
var count = 0
for item in s:
count += 1
doAssert count == 3, "Expected 3 items, got " & $count

View File

@@ -219,6 +219,3 @@ block: # bug #19531
x.cb()
y.cb()
block:
proc r(_: typedesc, _: static uint | static int) = discard; r(uint, 0)

View File

@@ -1,16 +0,0 @@
discard """
output:
ok
"""
type
Meters = distinct float
Feet = distinct float
converter toMeters(f: Feet): Meters =
Meters(float(f) * 0.3048)
proc showMeters(m: Meters) =
echo "ok"
showMeters(Feet(10.0))

View File

@@ -1,81 +0,0 @@
discard """
output: '''
42
5
3
2
1.0
2.0
55
'''
"""
# Object variant / case object
type
NodeKind = enum
nkInt, nkStr, nkAdd
Node = object
case kind: NodeKind
of nkInt: intVal: int
of nkStr: strVal: string
of nkAdd: left, right: ref Node
proc newInt(v: int): ref Node =
new(result)
result[] = Node(kind: nkInt, intVal: v)
let n = newInt(42)
echo n.intVal
# Sink and move semantics
type
BigObj = object
data: seq[int]
proc consume(x: sink BigObj) =
echo x.data.len
var b = BigObj(data: @[1, 2, 3, 4, 5])
consume(move b)
proc divmod(a, b: int): (int, int) =
(a div b, a mod b)
let (q, r) = divmod(17, 5)
echo q
echo r
# Shallow object with seq (trigger GC interaction)
type
Matrix = object
rows, cols: int
data: seq[float]
proc newMatrix(r, c: int): Matrix =
Matrix(rows: r, cols: c, data: newSeq[float](r * c))
proc `[]`(m: Matrix, r, c: int): float =
m.data[r * m.cols + c]
proc `[]=`(m: var Matrix, r, c: int, v: float) =
m.data[r * m.cols + c] = v
var m = newMatrix(2, 2)
m[0, 0] = 1.0
m[1, 1] = 2.0
echo m[0, 0]
echo m[1, 1]
template compute(body: untyped): int =
block:
body
let x = compute:
var sum = 0
for i in 1..10: sum += i
sum
echo x

View File

@@ -1,23 +0,0 @@
# Regression test for bug #21242
discard """
action: compile
"""
iterator iterSome(): int =
proc inner1() =
let something = 6
proc inner2() =
let othersomething = something
inner2()
for n in 0 .. 10:
inner1()
yield n
proc test() =
proc test1() =
for v in iterSome():
discard
test1()
test()

View File

@@ -1,13 +0,0 @@
import std/[sugar, strutils]
type Res[T] = tuple[a: int, b: string]
iterator test(): Res[string] =
yield (1, "")
for (i, s) in test():
static:
echo typeof(i)
echo typeof(s)
let
a: int = i
b: string = s

View File

@@ -1,6 +0,0 @@
discard """
action: reject
errormsg: "attempting to call routine: 'items'"
"""
let chars = "abc".items()

View File

@@ -1,50 +0,0 @@
discard """
action: "run"
"""
import std/[assertions, options, strutils]
from std/sequtils import toSeq
# block: # TODO: make iterable accept closure iterators?
# template mymap[T, U](s: iterable[T], f: proc(x: T): U): untyped =
# let res = iterator (): U =
# for val in s:
# yield f(val)
# res
# proc foo(x: string): string = x & "0"
# let a = "1\n2\n3\n4".splitLines().mymap(foo).toSeq()
# echo a
# echo typeof(a)
block splitIterable: # #22098
template collect[T](it: iterable[T]): seq[T] =
var res: seq[T] = @[]
for x in it:
res.add x
res
const text = "a b c d"
let words = text.split.collect()
doAssert words == @["a", "b", "c", "d"]
block optionElements:
iterator its(_: int; default: Option[string] = none(string)): Option[string] =
yield some("x")
var fromCall = none(string)
for x in its(0):
fromCall = x
doAssert fromCall == some("x")
var fromDot = none(string)
for x in 0.its:
fromDot = x
doAssert fromDot == some("x")
block closureIteratorCallsStayCallable:
let next = iterator (): string =
yield "x"
doAssert next() == "x"

View File

@@ -559,21 +559,17 @@ block: # void iterator
discard
var a = it
block:
# Locals present in only 1 state should be on the stack
block: # Locals present in only 1 state should be on the stack
proc checkOnStack(a: pointer, shouldBeOnStack: bool) =
# bug #25596: the very fact we take the address prevents the local
# from being on the stack
when false:
# Quick and dirty way to check if a points to stack
var dummy = 0
let dummyAddr = addr dummy
let distance = abs(cast[int](dummyAddr) - cast[int](a))
const requiredDistance = 300
if shouldBeOnStack:
doAssert(distance <= requiredDistance, "a is not on stack, but should")
else:
doAssert(distance > requiredDistance, "a is on stack, but should not")
# Quick and dirty way to check if a points to stack
var dummy = 0
let dummyAddr = addr dummy
let distance = abs(cast[int](dummyAddr) - cast[int](a))
const requiredDistance = 300
if shouldBeOnStack:
doAssert(distance <= requiredDistance, "a is not on stack, but should")
else:
doAssert(distance > requiredDistance, "a is on stack, but should not")
iterator it(): int {.closure.} =
var a = 1

View File

@@ -23,25 +23,3 @@ block:
doAssert x(a) == 1
doAssert y(a) == 1
import std/tables
block:
type
R = proc(): lent O {.nimcall.}
F = object
schema: R
O = object
fields: Table[string, F]
func f(o: O, key: string): R =
if key in o.fields: o.fields[key].schema
else: nil
block:
type
R = proc(): lent O
O = object
r: R
func f(o: O): int = 42

View File

@@ -1,39 +0,0 @@
discard """
nimout: '''
ObjectTy
Empty
Empty
RecList
IdentDefs
Sym "noDefault"
Sym "int"
Empty
IdentDefs
Sym "withDefault"
Sym "string"
StrLit "Hello World"
ProcTy
FormalParams
Sym "bool"
IdentDefs
Sym "foo"
Sym "string"
StrLit "Proc default"
Empty
'''
"""
import std/macros
type
FooBar = object
noDefault: int
withDefault = "Hello World"
SomeProc = proc (foo = "Proc default"): bool
macro dumpBodies() =
echo bindSym("FooBar").getTypeImpl().treeRepr
echo bindSym("SomeProc").getTypeImpl().treeRepr
dumpBodies()

View File

@@ -64,120 +64,3 @@ block: # bug #24683
cast[ptr int](addr x)[] = 10
doAssert x == @[1, 2, 3, 4, 45, 56, 67, 999, 88, 777]
when not defined(js):
block:
var x = high int
var result = x
# assert that multiplying highest int by highest int overflows
doAssertRaises(OverflowDefect):
x *= x
doAssertRaises(OverflowDefect):
result *= x
# overflow via compound assignment on int
var a = high(int)
doAssertRaises(OverflowDefect):
a += 1
var b = low(int)
doAssertRaises(OverflowDefect):
b -= 1
var c = high(int)
doAssertRaises(OverflowDefect):
c *= 2
# add smaller signed types too
var a8 = high(int8)
doAssertRaises(OverflowDefect):
a8 += 1
var b8 = low(int8)
doAssertRaises(OverflowDefect):
b8 -= 1
var c8 = high(int8)
doAssertRaises(OverflowDefect):
c8 *= 2
var a16 = high(int16)
doAssertRaises(OverflowDefect):
a16 += 1
var b16 = low(int16)
doAssertRaises(OverflowDefect):
b16 -= 1
# arithmetic operations that can overflow (non-compound direct ops)
doAssertRaises(OverflowDefect):
discard high(int) + 1
doAssertRaises(OverflowDefect):
discard low(int) - 1
doAssertRaises(OverflowDefect):
discard high(int) * 2
doAssertRaises(OverflowDefect):
discard low(int) div -1
# int8 overflow for signed operations
doAssertRaises(OverflowDefect):
discard high(int8) + 1'i8
doAssertRaises(OverflowDefect):
discard low(int8) - 1'i8
doAssertRaises(OverflowDefect):
discard high(int8) * 2'i8
# enum overflow, from arithmetics.succ/pred
type E = enum eA, eB
doAssertRaises(OverflowDefect):
discard eB.succ
doAssertRaises(OverflowDefect):
discard eA.pred
# floating-point compound divide should produce inf (not raise by defect)
var f = 1.0
f /= 0.0
# 1.0/0.0 is inf, check not finite
#doAssert not f.isFinite # `isFinite` not in this context, but avoid crash
# simple check ensures it mutated to a very large value
# (in Nim, `inf` is represented as 1e300*1e300; this compares as true)
doAssert f == 1.0 / 0.0
# Additional overflow cases across various integer widths
doAssertRaises(OverflowDefect):
discard high(int32) + 1'i32
doAssertRaises(OverflowDefect):
discard low(int32) - 1'i32
doAssertRaises(OverflowDefect):
discard high(int64) + 1'i64
doAssertRaises(OverflowDefect):
discard low(int64) - 1'i64
doAssertRaises(OverflowDefect):
discard -low(int64)
doAssertRaises(OverflowDefect):
discard abs(low(int8))
doAssertRaises(OverflowDefect):
discard high(int32) * 2'i32
doAssertRaises(OverflowDefect):
discard high(int64) * 2'i64
doAssertRaises(OverflowDefect):
discard low(int32) div -1'i32
doAssertRaises(OverflowDefect):
discard low(int64) div -1'i64

View File

@@ -130,9 +130,3 @@ block:
doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True"
doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability"
block: # bug #25674
var dict = newConfig()
dict.setSectionKey("", "key", "value\c")
var s = newStringStream()
dict.writeConfig(s)

View File

@@ -143,7 +143,3 @@ proc discardableCall(cmd: string): int {.discardable.} =
result = 123
discardableCall "echo hi"
block:
let a = "abc"
doAssert @a == @['a', 'b', 'c']

View File

@@ -4,7 +4,6 @@ empty
he, no return type;
abc a string
ha'''
target: "c js"
"""
proc ReturnT[T](x: T): T =
@@ -97,7 +96,3 @@ block: # typeof(stmt)
block:
template bad2 = echo (nonexistent; discard)
doAssert not compiles(bad2())
block:
discard default(tuple[b: void])
discard default((void,))

Some files were not shown because too many files have changed in this diff Show More