mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 03:13:41 +00:00
Compare commits
1 Commits
pr_so_so
...
pr_temp_in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c38fab3576 |
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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("}")
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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">☰</label>
|
||||
<label for="nav-toggle" id="nav-overlay"></label>
|
||||
<div class="container">
|
||||
<h1 class="title">$title</h1>$subtitle
|
||||
$content
|
||||
|
||||
@@ -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
|
||||
===================
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
102
doc/nimdoc.css
102
doc/nimdoc.css
@@ -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) {
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -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.
|
||||
|
||||
@@ -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] == '\"'):
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
##
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.} =
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)[]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.}
|
||||
|
||||
@@ -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.}
|
||||
@@ -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">☰</label>
|
||||
<label for="nav-toggle" id="nav-overlay"></label>
|
||||
<div class="container">
|
||||
<h1 class="title">nimdoc/extlinks/util</h1>
|
||||
<div class="row">
|
||||
|
||||
@@ -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">☰</label>
|
||||
<label for="nav-toggle" id="nav-overlay"></label>
|
||||
<div class="container">
|
||||
<h1 class="title">Nothing User Manual</h1>
|
||||
|
||||
|
||||
@@ -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">☰</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>
|
||||
|
||||
|
||||
|
||||
@@ -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">☰</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>
|
||||
|
||||
|
||||
|
||||
@@ -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">☰</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>
|
||||
|
||||
@@ -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">☰</label>
|
||||
<label for="nav-toggle" id="nav-overlay"></label>
|
||||
<div class="container">
|
||||
<h1 class="title">Not a Nim Manual</h1>
|
||||
<div class="row">
|
||||
|
||||
@@ -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">☰</label>
|
||||
<label for="nav-toggle" id="nav-overlay"></label>
|
||||
<div class="container">
|
||||
<h1 class="title">nimdoc/test_doctype/test_doctype</h1>
|
||||
<div class="row">
|
||||
|
||||
@@ -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">☰</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">
|
||||
|
||||
@@ -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">☰</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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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">☰</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>
|
||||
|
||||
|
||||
|
||||
@@ -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">☰</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">"FooBuzz msg"</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">"FooBuzz msg"</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
|
||||
|
||||
@@ -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">☰</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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -219,6 +219,3 @@ block: # bug #19531
|
||||
x.cb()
|
||||
y.cb()
|
||||
|
||||
|
||||
block:
|
||||
proc r(_: typedesc, _: static uint | static int) = discard; r(uint, 0)
|
||||
|
||||
@@ -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))
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -1,6 +0,0 @@
|
||||
discard """
|
||||
action: reject
|
||||
errormsg: "attempting to call routine: 'items'"
|
||||
"""
|
||||
|
||||
let chars = "abc".items()
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -143,7 +143,3 @@ proc discardableCall(cmd: string): int {.discardable.} =
|
||||
result = 123
|
||||
|
||||
discardableCall "echo hi"
|
||||
|
||||
block:
|
||||
let a = "abc"
|
||||
doAssert @a == @['a', 'b', 'c']
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user