Compare commits

..

2 Commits

Author SHA1 Message Date
ringabout
b786db630b fixes #25637; nim ic with destructors 2026-04-09 10:23:14 +08:00
ringabout
d914a7a81a optimizes setLen for orc; disabling ranging checks 2026-04-09 10:09:28 +08:00
65 changed files with 240 additions and 1926 deletions

View File

@@ -60,7 +60,7 @@ jobs:
run: nim c -r -d:release ci/action.nim run: nim c -r -d:release ci/action.nim
- name: 'Comment' - name: 'Comment'
uses: actions/github-script@v9 uses: actions/github-script@v8
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');

View File

@@ -60,23 +60,17 @@ errors.
- `copyDirWithPermissions` to recursively preserve attributes - `copyDirWithPermissions` to recursively preserve attributes
- `system.setLenUninit` now supports refc, JS and VM backends. - `system.setLenUninit` now supports refc, JS and VM backends.
- `system.setLenUninit` for the `string` type. Allows setting length without initializing new memory on growth.
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum. - `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
Modes include `Nim` (default, fully compatible) and two new experimental modes: Modes include `Nim` (default, fully compatible) and two new experimental modes:
`Lax` and `Gnu` for different option parsing behaviors. `Lax` and `Gnu` for different option parsing behaviors.
- `std/nre2` is added to replace deprecated NRE.
[//]: # "Changes:" [//]: # "Changes:"
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type. - `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function. - `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation. - `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`. - `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
Use https://github.com/nitely/nim-regex or `std/nre2`.
See: https://github.com/nim-lang/Nim/issues/23668.
## Language changes ## Language changes

View File

@@ -230,11 +230,11 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
of tyString, tySequence: of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst) let atyp = skipTypes(a.t, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.usesSso(): optSeqDestructors in p.config.globalOptions and not p.config.isDefined("nimsso"):
let bra = byRefLoc(p, a) let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"), p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra) bra)
if p.config.usesSso() and if p.config.isDefined("nimsso") and
skipTypes(a.t, abstractVar + abstractInst).kind == tyString: skipTypes(a.t, abstractVar + abstractInst).kind == tyString:
let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra
else: addrLoc(p.config, a) else: addrLoc(p.config, a)
@@ -296,11 +296,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
of tyString, tySequence: of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst) let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.usesSso(): optSeqDestructors in p.config.globalOptions and not p.config.isDefined("nimsso"):
let bra = byRefLoc(p, a) let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"), p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra) bra)
if p.config.usesSso() and if p.config.isDefined("nimsso") and
skipTypes(n.typ, abstractVar + abstractInst).kind == tyString: skipTypes(n.typ, abstractVar + abstractInst).kind == tyString:
if ntyp.kind in {tyVar} and not compileToCpp(p.module): if ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc let ra = a.rdLoc
@@ -335,7 +335,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
let ra = a.rdLoc let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra)) var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t) let lt = lenExpr(p, t)
if p.config.usesSso(): if p.config.isDefined("nimsso"):
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra)) result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator() result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet)) result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet))
@@ -370,7 +370,7 @@ proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} = proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
var a = initLocExpr(p, n[0]) var a = initLocExpr(p, n[0])
let tmp = withTmpIfNeeded(p, a, needsTmp) let tmp = withTmpIfNeeded(p, a, needsTmp)
let ra = if p.config.usesSso(): byRefLoc(p, tmp) else: tmp.rdLoc let ra = if p.config.isDefined("nimsso"): addrLoc(p.config, tmp) else: tmp.rdLoc
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra) result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) = proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =

View File

@@ -322,7 +322,7 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
bra) bra)
let rd = d.rdLoc let rd = d.rdLoc
let la = lenExpr(p, a) let la = lenExpr(p, a)
if p.config.usesSso(): if p.config.isDefined("nimsso"):
let bra = byRefLoc(p, a) let bra = byRefLoc(p, a)
p.s(cpsStmts).addFieldAssignment(rd, "Field0", p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cCall(cgsymValue(p.module, "nimStrData"), bra)) cCall(cgsymValue(p.module, "nimStrData"), bra))
@@ -963,7 +963,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
proc cowBracket(p: BProc; n: PNode) = proc cowBracket(p: BProc; n: PNode) =
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and
not p.config.usesSso(): not p.config.isDefined("nimsso"):
let strCandidate = n[0] let strCandidate = n[0]
if strCandidate.typ.skipTypes(abstractInst).kind == tyString: if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
var a: TLoc = initLocExpr(p, strCandidate) var a: TLoc = initLocExpr(p, strCandidate)
@@ -989,7 +989,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# bug #19497 # bug #19497
d.lode = e d.lode = e
else: else:
let ssoStrSub = p.config.usesSso() and e[0].kind == nkBracketExpr and let ssoStrSub = p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString 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 ssoStrSub: {lfEnforceDeref, lfPrepareForMutation} else: {})
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]): if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
@@ -1318,7 +1318,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.snippet = cDeref(a.snippet) a.snippet = cDeref(a.snippet)
if p.config.usesSso() and ty.kind == tyString: if p.config.isDefined("nimsso") and ty.kind == tyString:
let bra = byRefLoc(p, a) let bra = byRefLoc(p, a)
if lfPrepareForMutation in d.flags: if lfPrepareForMutation in d.flags:
# Use nimStrAtMutV3 to get a mutable reference (char*) to the element. # Use nimStrAtMutV3 to get a mutable reference (char*) to the element.
@@ -1889,17 +1889,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
var t = e.typ.skipTypes(abstractInstOwned) var t = e.typ.skipTypes(abstractInstOwned)
let isRef = t.kind == tyRef let isRef = t.kind == tyRef
# check if we need to construct the object in a temporary. # check if we need to construct the object in a temporary
# A temp is needed when:
# - the constructor produces a ref (isRef)
# - the destination is not a writable location (d.k == locNone)
# - the constructed type differs from the destination type (subtype
# assignments need the genAssignment path for ObjectAssignmentDefect)
# - the constructor's field values may alias the destination (isPartOf)
var useTemp = var useTemp =
isRef or isRef or
d.k == locNone or (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
(d.t != nil and not sameBackendType(t, d.t.skipTypes(abstractInstOwned))) or
(isPartOf(d.lode, e) != arNo) (isPartOf(d.lode, e) != arNo)
var tmp: TLoc = default(TLoc) var tmp: TLoc = default(TLoc)
@@ -2150,7 +2143,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage) putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage)
of tyString, tySequence: of tyString, tySequence:
let la = lenExpr(p, a) let la = lenExpr(p, a)
if p.config.usesSso() and if p.config.isDefined("nimsso") and
skipTypes(a.t, abstractVarRange).kind == tyString: skipTypes(a.t, abstractVarRange).kind == tyString:
let bra = byRefLoc(p, a) let bra = byRefLoc(p, a)
putIntoDest(p, b, e, putIntoDest(p, b, e,
@@ -2743,7 +2736,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc = initLocExpr(p, n[0]) var a: TLoc = initLocExpr(p, n[0])
let arg = if p.config.usesSso(): byRefLoc(p, a) else: rdLoc(a) let arg = if p.config.isDefined("nimsso"): addrLoc(p.config, a) else: rdLoc(a)
putIntoDest(p, d, n, putIntoDest(p, d, n,
cgCall(p, "nimToCStringConv", arg), cgCall(p, "nimToCStringConv", arg),
a.storage) a.storage)
@@ -2822,7 +2815,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
var src: TLoc = initLocExpr(p, n[2]) var src: TLoc = initLocExpr(p, n[2])
let destVal = rdLoc(a) let destVal = rdLoc(a)
let srcVal = rdLoc(src) let srcVal = rdLoc(src)
if p.config.usesSso() and if p.config.isDefined("nimsso") and
n[1].typ.skipTypes(abstractVar).kind == tyString: n[1].typ.skipTypes(abstractVar).kind == tyString:
# SmallString: destroy dst then struct-copy src; no .p field aliasing needed # SmallString: destroy dst then struct-copy src; no .p field aliasing needed
genStmts(p, n[3]) genStmts(p, n[3])
@@ -2871,7 +2864,7 @@ proc genDestroy(p: BProc; n: PNode) =
case t.kind case t.kind
of tyString: of tyString:
var a: TLoc = initLocExpr(p, arg) var a: TLoc = initLocExpr(p, arg)
if p.config.usesSso(): if p.config.isDefined("nimsso"):
# SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings) # SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a)) p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a))
else: else:
@@ -4243,7 +4236,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
genConstObjConstr(p, n, isConst, result) genConstObjConstr(p, n, isConst, result)
of tyString, tyCstring: of tyString, tyCstring:
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
if p.config.usesSso(): if p.config.isDefined("nimsso"):
genStringLiteralV3Const(p.module, n, isConst, result) genStringLiteralV3Const(p.module, n, isConst, result)
else: else:
genStringLiteralV2Const(p.module, n, isConst, result) genStringLiteralV2Const(p.module, n, isConst, result)

View File

@@ -22,7 +22,7 @@ template detectVersion(field, corename) =
result = 1 result = 1
proc detectStrVersion(m: BModule): int = proc detectStrVersion(m: BModule): int =
if m.g.config.usesSso() and if m.g.config.isDefined("nimsso") and
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}: m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
result = 3 result = 3
else: else:

View File

@@ -1165,7 +1165,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
throw; throw;
} }
} catch(...) { } catch(...) {
// C++ exception occurred, not under Nim's control. // C++ exception occured, not under Nim's control.
} }
{ {
/* finally: */ /* finally: */
@@ -1940,7 +1940,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
elif optFieldCheck in p.options and isDiscriminantField(e[0]): elif optFieldCheck in p.options and isDiscriminantField(e[0]):
genLineDir(p, e) genLineDir(p, e)
asgnFieldDiscriminant(p, e) asgnFieldDiscriminant(p, e)
elif p.config.usesSso() and e[0].kind == nkBracketExpr and elif p.config.isDefined("nimsso") and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString: e[0][0].typ.skipTypes(abstractVar).kind == tyString:
# nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally) # nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally)
genLineDir(p, e) genLineDir(p, e)

View File

@@ -389,7 +389,7 @@ proc lenField(p: BProc, val: Rope): Rope {.inline.} =
proc lenExpr(p: BProc; a: TLoc): Rope = proc lenExpr(p: BProc; a: TLoc): Rope =
if optSeqDestructors in p.config.globalOptions: if optSeqDestructors in p.config.globalOptions:
if p.config.usesSso() and a.lode != nil and a.t != nil and if p.config.isDefined("nimsso") and a.lode != nil and a.t != nil and
a.t.skipTypes(abstractInst).kind == tyString: a.t.skipTypes(abstractInst).kind == tyString:
result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a)) result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a))
else: else:
@@ -534,7 +534,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
let atyp = skipTypes(loc.t, abstractInst) let atyp = skipTypes(loc.t, abstractInst)
let rl = rdLoc(loc) let rl = rdLoc(loc)
if typ.kind == tyString and p.config.usesSso(): if typ.kind == tyString and p.config.isDefined("nimsso"):
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed) # SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
if atyp.kind in {tyVar, tyLent}: if atyp.kind in {tyVar, tyLent}:
p.s(cpsStmts).addAssignment(derefField(rl, "bytes"), cIntValue(0)) p.s(cpsStmts).addAssignment(derefField(rl, "bytes"), cIntValue(0))
@@ -592,7 +592,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
let typ = loc.t let typ = loc.t
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}: if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
let rl = rdLoc(loc) let rl = rdLoc(loc)
if skipTypes(typ, abstractInst + {tyStatic}).kind == tyString and p.config.usesSso(): 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) # 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, "bytes", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil) p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil)

View File

@@ -250,7 +250,6 @@ const
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found" errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found" errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found" errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
errDefaultOrSsoExpectedButXFound = "'default' or 'sso' expected, but '$1' found"
template warningOptionNoop(switch: string) = template warningOptionNoop(switch: string) =
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch) warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -307,13 +306,6 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
else: else:
result = false result = false
localError(conf, info, errInvalidExceptionSystem % arg) localError(conf, info, errInvalidExceptionSystem % arg)
of "strings":
case arg.normalize
of "default": result = conf.selectedStrings == stringDefault
of "sso": result = conf.selectedStrings == stringSso
else:
result = false
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
of "experimental": of "experimental":
try: try:
result = conf.features.contains parseEnum[Feature](arg) result = conf.features.contains parseEnum[Feature](arg)
@@ -758,17 +750,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processMemoryManagementOption(switch, arg, pass, info, conf) processMemoryManagementOption(switch, arg, pass, info, conf)
of "mm": of "mm":
processMemoryManagementOption(switch, arg, pass, info, conf) processMemoryManagementOption(switch, arg, pass, info, conf)
of "strings":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
case arg.normalize
of "default":
conf.selectedStrings = stringDefault
of "sso":
conf.selectedStrings = stringSso
defineSymbol(conf.symbols, "nimsso")
else:
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
of "warnings", "w": of "warnings", "w":
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf) if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf) of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)

View File

@@ -10,10 +10,10 @@
## Generate a .build.nif file for nifmake from a Nim project. ## Generate a .build.nif file for nifmake from a Nim project.
## This enables incremental and parallel compilation using the `m` switch. ## This enables incremental and parallel compilation using the `m` switch.
import std / [os, tables, sets, times, osproc] import std / [os, tables, sets, times, osproc, strutils]
import options, msgs, lineinfos, pathutils import options, msgs, lineinfos, pathutils
import "../dist/nimony/src/lib" / [nifstreams, bitabs, nifreader, nifbuilder] import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
import "../dist/nimony/src/gear2" / modnames import "../dist/nimony/src/gear2" / modnames
type type
@@ -79,19 +79,22 @@ proc runNifler(c: DepContext; nimFile: string): bool =
let exitCode = execShellCmd(cmd) let exitCode = execShellCmd(cmd)
result = exitCode == 0 result = exitCode == 0
proc resolveImport(c: DepContext; origin, toResolve: string): string = proc resolveFile(c: DepContext; origin, toResolve: string): string =
## Resolve an import path using the compiler's normal module lookup rules. ## Resolve an import path relative to origin file
result = findModule(c.config, toResolve, origin).string # Handle std/ prefix
var path = toResolve
if path.startsWith("std/"):
path = path.substr(4)
proc resolveInclude(c: DepContext; origin, toResolve: string): string = # Try relative to origin first
## Resolve an include path relative to the including file or the search paths.
let originDir = parentDir(origin) let originDir = parentDir(origin)
result = originDir / toResolve.addFileExt("nim") result = originDir / path.addFileExt("nim")
if fileExists(result): if fileExists(result):
return result return result
# Try search paths
for searchPath in c.config.searchPaths: for searchPath in c.config.searchPaths:
result = searchPath.string / toResolve.addFileExt("nim") result = searchPath.string / path.addFileExt("nim")
if fileExists(result): if fileExists(result):
return result return result
@@ -100,7 +103,7 @@ proc resolveInclude(c: DepContext; origin, toResolve: string): string =
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
proc processInclude(c: var DepContext; includePath: string; current: Node) = proc processInclude(c: var DepContext; includePath: string; current: Node) =
let resolved = resolveInclude(c, current.files[current.files.len - 1].nimFile, includePath) let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
if resolved.len == 0 or not fileExists(resolved): if resolved.len == 0 or not fileExists(resolved):
return return
@@ -115,7 +118,7 @@ proc processInclude(c: var DepContext; includePath: string; current: Node) =
discard c.includeStack.pop() discard c.includeStack.pop()
proc processImport(c: var DepContext; importPath: string; current: Node) = proc processImport(c: var DepContext; importPath: string; current: Node) =
let resolved = resolveImport(c, current.files[0].nimFile, importPath) let resolved = resolveFile(c, current.files[0].nimFile, importPath)
if resolved.len == 0 or not fileExists(resolved): if resolved.len == 0 or not fileExists(resolved):
return return
@@ -137,171 +140,6 @@ proc processImport(c: var DepContext; importPath: string; current: Node) =
if existingIdx notin current.deps: if existingIdx notin current.deps:
current.deps.add existingIdx current.deps.add existingIdx
proc skipSubtree(s: var Stream; first: PackedToken) =
## Consume tokens until the ParLe at `first` is balanced. Caller has
## already obtained `first`.
if first.kind != ParLe: return
var depth = 1
while depth > 0:
let t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
elif t.kind == EofToken: return
proc evalCondExpr(c: DepContext; s: var Stream): bool =
## Read exactly one condition expression from `s` and return its truth
## value. Consumes tokens whether the expression is recognised or not so
## the caller stays in sync. Recognises `defined(IDENT)`, the boolean
## operators `not`/`and`/`or`, and the literals `true`/`false`. Anything
## else (e.g. a call to an arbitrary proc) is treated as `true` — the
## conservative direction, since a false negative here drops a real
## dependency from the build graph.
let t = next(s)
case t.kind
of Ident:
case pool.strings[t.litId]
of "true": result = true
of "false": result = false
else: result = true
of ParLe:
let tag = pool.tags[t.tagId]
case tag
of "call", "cmd", "callstrlit", "infix", "prefix":
# First child is the head (function/operator name).
let head = next(s)
var name = ""
if head.kind == Ident: name = pool.strings[head.litId]
case name
of "defined":
let arg = next(s)
var sym = ""
if arg.kind == Ident: sym = pool.strings[arg.litId]
result = sym.len > 0 and isDefined(c.config, sym)
of "not":
result = not evalCondExpr(c, s)
of "and":
result = evalCondExpr(c, s)
if result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
of "or":
result = evalCondExpr(c, s)
if not result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
else:
result = true
# Drain whatever remains until the matching ParRi.
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
of "not":
result = not evalCondExpr(c, s)
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
of "and":
result = evalCondExpr(c, s)
if result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
# consume closing ParRi
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
of "or":
result = evalCondExpr(c, s)
if not result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
else:
skipSubtree(s, t)
result = true
else:
result = true
proc whenMarkerHolds(c: DepContext; s: var Stream): bool =
## Caller has just consumed the `(when` ParLe. Read children until the
## matching `)`, AND-ing each evaluated condition.
result = true
while true:
# peek by reading; if it's ParRi, we're done
let t = next(s)
if t.kind == ParRi: return
if t.kind == EofToken: return
if t.kind == ParLe:
# Re-feed by manually evaluating the subtree starting at `t`.
# evalCondExpr expects to read its own opener, so handle it directly.
let tag = pool.tags[t.tagId]
case tag
of "call", "cmd", "callstrlit", "infix", "prefix":
let head = next(s)
var name = ""
if head.kind == Ident: name = pool.strings[head.litId]
var ok = true
case name
of "defined":
let arg = next(s)
var sym = ""
if arg.kind == Ident: sym = pool.strings[arg.litId]
ok = sym.len > 0 and isDefined(c.config, sym)
of "not":
ok = not evalCondExpr(c, s)
of "and":
ok = evalCondExpr(c, s)
if ok: ok = evalCondExpr(c, s)
of "or":
ok = evalCondExpr(c, s)
if not ok: ok = evalCondExpr(c, s)
else:
ok = true
# finish the subtree
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
if not ok: result = false
of "not", "and", "or":
# Re-emit a synthetic dispatch: rewrap by descending.
var ok = true
case tag
of "not":
ok = not evalCondExpr(c, s)
of "and":
ok = evalCondExpr(c, s)
if ok: ok = evalCondExpr(c, s)
of "or":
ok = evalCondExpr(c, s)
if not ok: ok = evalCondExpr(c, s)
else: discard
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
if not ok: result = false
else:
# Unknown — treat as true and skip.
skipSubtree(s, t)
elif t.kind == Ident:
let v = pool.strings[t.litId]
if v == "false": result = false
# else (true / unknown ident): keep result
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) = proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
## Read a .deps.nif file and process imports/includes ## Read a .deps.nif file and process imports/includes
let depsPath = c.depsFile(pair) let depsPath = c.depsFile(pair)
@@ -323,27 +161,12 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
if t.kind == ParLe: if t.kind == ParLe:
let tag = pool.tags[t.tagId] let tag = pool.tags[t.tagId]
case tag case tag
of "import", "fromimport", "include": of "import", "fromimport":
# Read first child. May be a `(when COND...)` marker — parse and # Read import path
# evaluate; if the condition is statically false, skip the import
# entirely. Otherwise advance past the marker and parse the path.
t = next(s) t = next(s)
var live = true # Check for "when" marker (conditional import)
if t.kind == ParLe and pool.tags[t.tagId] == "when": if t.kind == Ident and pool.strings[t.litId] == "when":
# whenMarkerHolds consumes everything up to and including the t = next(s) # skip it, still process the import
# closing `)` of the `(when ...)` subtree.
live = whenMarkerHolds(c, s)
t = next(s)
if not live:
# Drain the rest of this import/include node.
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: break
t = next(s)
continue
# Handle path expression (could be ident, string, or infix like std/foo) # Handle path expression (could be ident, string, or infix like std/foo)
var importPath = "" var importPath = ""
if t.kind == Ident: if t.kind == Ident:
@@ -361,11 +184,26 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
if t.kind == Ident: # second part (foo) if t.kind == Ident: # second part (foo)
importPath = importPath & "/" & pool.strings[t.litId] importPath = importPath & "/" & pool.strings[t.litId]
if importPath.len > 0: if importPath.len > 0:
if tag == "include": processImport(c, importPath, current)
processInclude(c, importPath, current) # Skip to end of import node
else: var depth = 1
processImport(c, importPath, current) while depth > 0:
# Skip to end of node t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
of "include":
# Read include path
t = next(s)
if t.kind == Ident and pool.strings[t.litId] == "when":
t = next(s) # skip conditional marker
var includePath = ""
if t.kind == Ident:
includePath = pool.strings[t.litId]
elif t.kind == StringLit:
includePath = pool.strings[t.litId]
if includePath.len > 0:
processInclude(c, includePath, current)
# Skip to end
var depth = 1 var depth = 1
while depth > 0: while depth > 0:
t = next(s) t = next(s)

View File

@@ -1544,7 +1544,7 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
r.res = s.loc.snippet r.res = s.loc.snippet
r.address = "" r.address = ""
r.typ = etyNone r.typ = etyNone
of skVar, skLet, skResult, skTemp, skForVar: of skVar, skLet, skResult:
r.kind = resExpr r.kind = resExpr
let jsType = mapType(p): let jsType = mapType(p):
if typ.isNil: if typ.isNil:

View File

@@ -592,12 +592,10 @@ proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x)) result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
result.add lenCall result.add lenCall
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode; noinit = false): PNode = proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
let lenCall = genBuiltin(c, mLengthSeq, "len", y) let lenCall = genBuiltin(c, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt) lenCall.typ = getSysType(c.g, x.info, tyInt)
let name = if noinit: "setLenUninit" else: "setLen" var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
let magic = if noinit: mSetLengthSeqUninit else: mSetLengthSeq
var op = getSysMagic(c.g, x.info, name, magic)
op = instantiateGeneric(c, op, t, t) op = instantiateGeneric(c, op, t, t)
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall) result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
@@ -645,9 +643,8 @@ proc genBulkCopySeq(c: var TLiftCtx; t: PType; body, x, y: PNode) =
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind case c.kind
of attachedDup: of attachedDup:
let bulkCopy = supportsCopyMem(t.elementType) body.add setLenSeqCall(c, t, x, y)
body.add setLenSeqCall(c, t, x, y, noinit = bulkCopy) if supportsCopyMem(t.elementType):
if bulkCopy:
genBulkCopySeq(c, t, body, x, y) genBulkCopySeq(c, t, body, x, y)
else: else:
forallElements(c, t, body, x, y) forallElements(c, t, body, x, y)
@@ -661,9 +658,8 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# This is usually more efficient than a destroy/create pair. # This is usually more efficient than a destroy/create pair.
# For trivially copyable types, use bulk copyMem instead of element loop. # For trivially copyable types, use bulk copyMem instead of element loop.
checkSelfAssignment(c, t, body, x, y) checkSelfAssignment(c, t, body, x, y)
let bulkCopy = supportsCopyMem(t.elementType) body.add setLenSeqCall(c, t, x, y)
body.add setLenSeqCall(c, t, x, y, noinit = bulkCopy) if supportsCopyMem(t.elementType):
if bulkCopy:
genBulkCopySeq(c, t, body, x, y) genBulkCopySeq(c, t, body, x, y)
else: else:
forallElements(c, t, body, x, y) forallElements(c, t, body, x, y)
@@ -732,7 +728,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedAsgn, attachedDeepCopy, attachedDup: of attachedAsgn, attachedDeepCopy, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y) body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
of attachedSink: of attachedSink:
if c.g.config.usesSso(): if c.g.config.isDefined("nimsso"):
# SmallString: destroy old dst, then bit-copy src (no rc increment — this is a move). # 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. # No .p aliasing check needed; rc-based destroy handles COW sharing correctly.
doAssert t.destructor != nil doAssert t.destructor != nil

View File

@@ -12,8 +12,7 @@ define:nimPreviewNonVarDestructor
define:nimPreviewCheckedClose define:nimPreviewCheckedClose
define:nimPreviewAsmSemSymbol define:nimPreviewAsmSemSymbol
define:nimPreviewCStringComparisons define:nimPreviewCStringComparisons
#define:nimPreviewDuplicateModuleError define:nimPreviewDuplicateModuleError
# Incompatible with Nimony's compat2.nim for now
threads:off threads:off

View File

@@ -121,11 +121,6 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}: conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}:
initOrcDefines(conf) initOrcDefines(conf)
if conf.selectedStrings == stringSso and
conf.selectedGC notin {gcArc, gcOrc, gcYrc, gcAtomicArc}:
rawMessage(conf, errGenerated,
"--strings:sso requires --mm:arc, --mm:orc, --mm:yrc, or --mm:atomicArc")
mainCommand(graph) mainCommand(graph)
if conf.hasHint(hintGCStats): echo(GC_getStatistics()) if conf.hasHint(hintGCStats): echo(GC_getStatistics())
#echo(GC_getStatistics()) #echo(GC_getStatistics())

View File

@@ -267,10 +267,6 @@ type
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc, ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
StringsMode* = enum
stringDefault = "default"
stringSso = "sso"
ExceptionSystem* = enum ExceptionSystem* = enum
excNone, # no exception system selected yet excNone, # no exception system selected yet
excSetjmp, # setjmp based exception handling excSetjmp, # setjmp based exception handling
@@ -370,7 +366,6 @@ type
implicitCmd*: bool # whether some flag triggered an implicit `command` implicitCmd*: bool # whether some flag triggered an implicit `command`
selectedGC*: TGCMode # the selected GC (+) selectedGC*: TGCMode # the selected GC (+)
exc*: ExceptionSystem exc*: ExceptionSystem
selectedStrings*: StringsMode
hintProcessingDots*: bool # true for dots, false for filenames hintProcessingDots*: bool # true for dots, false for filenames
verbosity*: int # how verbose the compiler is verbosity*: int # how verbose the compiler is
numberOfProcessors*: int # number of processors numberOfProcessors*: int # number of processors
@@ -703,7 +698,6 @@ template quitOrRaise*(conf: ConfigRef, msg = "") =
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools} proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
proc usesSso*(conf: ConfigRef): bool {.inline.} = conf.selectedStrings == stringSso
template compilationCachePresent*(conf: ConfigRef): untyped = template compilationCachePresent*(conf: ConfigRef): untyped =
false false

View File

@@ -582,7 +582,6 @@ proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
inc(g.lineLen, s.len) inc(g.lineLen, s.len)
proc putComment(g: var TSrcGen, s: string) = proc putComment(g: var TSrcGen, s: string) =
const SpecialWhitespace = {' ', '\t', '\r', '\n', '\0'}
if s.len == 0: return if s.len == 0: return
var i = 0 var i = 0
let hi = s.len - 1 let hi = s.len - 1
@@ -612,12 +611,12 @@ proc putComment(g: var TSrcGen, s: string) =
# gets too long: # gets too long:
# compute length of the following word: # compute length of the following word:
var j = i var j = i
while j <= hi and s[j] notin SpecialWhitespace: inc(j) while j <= hi and s[j] > ' ': inc(j)
if not isCode and (g.col + (j - i) > MaxLineLen): if not isCode and (g.col + (j - i) > MaxLineLen):
put(g, tkComment, com) put(g, tkComment, com)
optNL(g, ind) optNL(g, ind)
com = "## " com = "## "
while i <= hi and s[i] notin SpecialWhitespace: while i <= hi and s[i] > ' ':
com.add(s[i]) com.add(s[i])
inc(i) inc(i)
put(g, tkComment, com) put(g, tkComment, com)

View File

@@ -131,7 +131,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var sym = syms[0].s var sym = syms[0].s
let name = sym.name let name = sym.name
var scope = syms[0].scope var scope = syms[0].scope
c.openShadowScope
if allowTypeBoundOps: if allowTypeBoundOps:
for a in 1 ..< n.len: for a in 1 ..< n.len:
# for every already typed argument, add type bound ops # for every already typed argument, add type bound ops
@@ -218,10 +218,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
scope = syms[nextSymIndex].scope scope = syms[nextSymIndex].scope
inc(nextSymIndex) inc(nextSymIndex)
if best.state == csMatch and best.calleeSym != nil and best.calleeSym.kind in {skTemplate, skMacro}:
c.closeShadowScope
else:
c.mergeShadowScope
proc effectProblem(f, a: PType; result: var string; c: PContext) = proc effectProblem(f, a: PType; result: var string; c: PContext) =
if f.kind == tyProc and a.kind == tyProc: if f.kind == tyProc and a.kind == tyProc:

View File

@@ -180,12 +180,9 @@ type
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
inUncheckedAssignSection*: int inUncheckedAssignSection*: int
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id]) importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
forwardTypeUpdates*: seq[(PSym, PType, PNode)] forwardTypeUpdates*: seq[(PType, PNode)]
# top-level owner, type, and type node for delayed retries inside a # types that need to be updated in a type section
# type section due to containing forward types # due to containing forward types, and their corresponding nodes
forwardFieldUpdates*: seq[(PType, PNode, PType)]
# object/tuple field definitions whose default values mention forward
# types and need delayed const checking
inTypeofContext*: int inTypeofContext*: int
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.} semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
@@ -778,17 +775,9 @@ proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
result[0] = newSymNode(op) result[0] = newSymNode(op)
analyseIfAddressTakenInCall(c, result, false) analyseIfAddressTakenInCall(c, result, false)
of attachedSink: of attachedSink:
result = n result = c.semAsgnOpr(c, n, nkSinkAsgn)
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)
of attachedAsgn: of attachedAsgn:
result = n result = c.semAsgnOpr(c, n, nkAsgn)
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)
of attachedDeepCopy: of attachedDeepCopy:
result = n result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink}) let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})

View File

@@ -963,15 +963,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
# echo "SUCCESS evaluated at compile time: ", call.renderTree # echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
let oldErrorCount = c.config.errorCounter
inc c.inStaticContext inc c.inStaticContext
openScope(c) openScope(c)
let a = semExprWithType(c, n, expectedType = expectedType) let a = semExprWithType(c, n, expectedType = expectedType)
closeScope(c) closeScope(c)
dec c.inStaticContext dec c.inStaticContext
if a.findUnresolvedStatic != nil or if a.findUnresolvedStatic != nil: return a
c.config.errorCounter != oldErrorCount:
return a
result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner) result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
if result.isNil: if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n)) localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))

View File

@@ -65,7 +65,7 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
t.incl tfNonConstExpr t.incl tfNonConstExpr
else: else:
t = base t = base
result.typ = makeTypeDesc(c, decayTypeOfView(c, t)) result.typ = makeTypeDesc(c, t)
type type
SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn
@@ -615,9 +615,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mAsgn: of mAsgn:
case n[0].sym.name.s case n[0].sym.name.s
of "=", "=copy": of "=", "=copy":
result = replaceHookMagic(c, n, attachedAsgn) result = semAsgnOpr(c, n, nkAsgn)
of "=sink": of "=sink":
result = replaceHookMagic(c, n, attachedSink) result = semAsgnOpr(c, n, nkSinkAsgn)
else: else:
result = semShallowCopy(c, n, flags) result = semShallowCopy(c, n, flags)
of mIsPartOf: result = semIsPartOf(c, n, flags) of mIsPartOf: result = semIsPartOf(c, n, flags)

View File

@@ -815,14 +815,6 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy
lastDef[^1] = val lastDef[^1] = val
result.add(lastDef) result.add(lastDef)
proc materializeDirectView(n: PNode): PNode =
let t = n.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
if t.kind in {tyVar, tyLent}:
result = newNodeIT(nkHiddenDeref, n.info, t.elementType)
result.add n
else:
result = n
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
result = copyNode(n) result = copyNode(n)
@@ -889,10 +881,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
#changeType(def.skipConv, typ, check=true) #changeType(def.skipConv, typ, check=true)
else: else:
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen) typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
let directTyp = typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
if directTyp.kind in {tyVar, tyLent}:
def = materializeDirectView(def)
typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
typ = typ.last typ = typ.last
if hasEmpty(typ): if hasEmpty(typ):
@@ -920,8 +908,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
if c.matchedConcept != nil: if c.matchedConcept != nil:
typFlags.incl taConcept typFlags.incl taConcept
if a.kind != nkVarTuple: typeAllowedCheck(c, a.info, typ, symkind, typFlags)
typeAllowedCheck(c, a.info, typ, symkind, typFlags)
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:
@@ -1821,35 +1808,15 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
internalAssert c.config, false internalAssert c.config, false
proc typeSectionFinalPass(c: PContext, n: PNode) = proc typeSectionFinalPass(c: PContext, n: PNode) =
# each top level type needs to be processed, each epoch should reify at least one for (typ, typeNode) in c.forwardTypeUpdates:
var remainingOwners = initIntSet() # types that need to be updated due to containing forward types
for (owner, _, _) in c.forwardTypeUpdates: # and their corresponding type nodes
remainingOwners.incl owner.id # for example generic invocations of forward types end up here
var reified = semTypeNode(c, typeNode, nil)
while c.forwardTypeUpdates.len > 0: assert reified != nil
let pending = move c.forwardTypeUpdates assignType(typ, reified)
var madeProgress = false typ.itemId = reified.itemId # same id
c.forwardTypeUpdates = @[]
for (owner, typ, typeNode) in pending:
# types that need to be updated due to containing forward types
# and their corresponding type nodes
# for example generic invocations of forward types end up here
var reified = semTypeNode(c, typeNode, nil)
assert reified != nil
assignType(typ, reified)
typ.itemId = reified.itemId # same id
if containsForwardType(typ):
c.forwardTypeUpdates.add (owner, typ, typeNode)
elif not remainingOwners.missingOrExcl(owner.id):
madeProgress = true
if not madeProgress:
# can't error here unfortunately
break
for (owner, field, expectedType) in c.forwardFieldUpdates:
semDelayedFieldDefault(c, owner, expectedType, field)
c.forwardFieldUpdates = @[]
for i in 0..<n.len: for i in 0..<n.len:
var a = n[i] var a = n[i]
if a.kind == nkCommentStmt: continue if a.kind == nkCommentStmt: continue
@@ -2949,15 +2916,13 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
proc semStaticStmt(c: PContext, n: PNode): PNode = proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt" #echo "semStaticStmt"
#writeStackTrace() #writeStackTrace()
let oldErrorCount = c.config.errorCounter
inc c.inStaticContext inc c.inStaticContext
openScope(c) openScope(c)
let a = semStmt(c, n[0], {}) let a = semStmt(c, n[0], {})
closeScope(c) closeScope(c)
dec c.inStaticContext dec c.inStaticContext
n[0] = a n[0] = a
if c.config.errorCounter == oldErrorCount: evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
when false: when false:
# for incremental replays, keep the AST as required for replays: # for incremental replays, keep the AST as required for replays:
result = n result = n

View File

@@ -223,7 +223,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base) if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
if base.kind notin {tyGenericParam, tyGenericInvocation}: if base.kind notin {tyGenericParam, tyGenericInvocation}:
if base.kind == tyForward: if base.kind == tyForward:
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) c.forwardTypeUpdates.add (base, n[1])
elif not isOrdinalType(base, allowEnumWithHoles = true): elif not isOrdinalType(base, allowEnumWithHoles = true):
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc)) localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
elif lengthOrd(c.config, base) > MaxSetElements: elif lengthOrd(c.config, base) > MaxSetElements:
@@ -318,61 +318,6 @@ proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField}) typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
dec c.inStaticContext dec c.inStaticContext
proc containsForwardTypeAux(t: PType; seen: var IntSet): bool
proc containsForwardTypeAux(n: PNode; seen: var IntSet): bool =
result = false
if n.isNil or n.kind in nkLiterals + {nkNilLit, nkEmpty, nkType}:
return
if containsForwardTypeAux(n.typ, seen) or
(n.kind == nkSym and n.sym.typ != n.typ and containsForwardTypeAux(n.sym.typ, seen)):
return true
for i in 0 ..< n.safeLen:
if containsForwardTypeAux(n[i], seen):
return true
proc containsForwardTypeAux(t: PType; seen: var IntSet): bool =
result = false
if t.isNil:
return
if t.kind == tyForward:
return true
if not containsOrIncl(seen, t.id):
if containsForwardTypeAux(t.n, seen):
return true
for i in 0 ..< t.len:
if containsForwardTypeAux(t[i], seen):
return true
proc containsForwardType(arg: PNode): bool =
var seen = initIntSet()
containsForwardTypeAux(arg, seen)
proc containsForwardType(t: PType): bool =
var seen = initIntSet()
containsForwardTypeAux(t, seen)
proc semFieldDefault(c: PContext; owner, expectedType: PType; field: PNode): PType =
result = expectedType
field[^1] = semExprWithType(c, field[^1], {efDetermineType, efAllowSymChoice}, result)
if result == nil:
result = field[^1].typ
if c.inGenericContext == 0:
if containsForwardType(field[^1]):
c.forwardFieldUpdates.add (owner, field, result)
else:
fitDefaultNode(c, field[^1], result)
result = field[^1].typ.skipIntLit(c.idgen)
propagateToOwner(owner, result)
proc semDelayedFieldDefault(c: PContext; owner, expectedType: PType; field: PNode) =
fitDefaultNode(c, field[^1], expectedType)
propagateToOwner(owner, field[^1].typ.skipIntLit(c.idgen))
proc isRecursiveType*(t: PType): bool = proc isRecursiveType*(t: PType): bool =
# handle simple recusive types before typeFinalPass # handle simple recusive types before typeFinalPass
var cycleDetector = initIntSet() var cycleDetector = initIntSet()
@@ -605,7 +550,13 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
var hasDefaultField = a[^1].kind != nkEmpty var hasDefaultField = a[^1].kind != nkEmpty
if hasDefaultField: if hasDefaultField:
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
typ = semFieldDefault(c, result, typ, a) if c.inGenericContext > 0:
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = a[^1].typ
else:
fitDefaultNode(c, a[^1], typ)
typ = a[^1].typ.skipIntLit(c.idgen)
elif a[^2].kind != nkEmpty: elif a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil) typ = semTypeNode(c, a[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
@@ -971,7 +922,14 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
var hasDefaultField = n[^1].kind != nkEmpty var hasDefaultField = n[^1].kind != nkEmpty
if hasDefaultField: if hasDefaultField:
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
typ = semFieldDefault(c, rectype, typ, n) if c.inGenericContext > 0:
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = n[^1].typ
else:
fitDefaultNode(c, n[^1], typ)
typ = n[^1].typ.skipIntLit(c.idgen)
propagateToOwner(rectype, typ)
elif n[^2].kind == nkEmpty: elif n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected) localError(c.config, n.info, errTypeExpected)
typ = errorType(c) typ = errorType(c)
@@ -1114,7 +1072,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
if needsForwardUpdate: if needsForwardUpdate:
# if the inherited object is a forward type, # if the inherited object is a forward type,
# the entire object needs to be checked again # the entire object needs to be checked again
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass c.forwardTypeUpdates.add (result, n) # we retry in the final pass
rawAddSon(result, realBase) rawAddSon(result, realBase)
if realBase == nil and tfInheritable in flags: if realBase == nil and tfInheritable in flags:
result.incl tfInheritable result.incl tfInheritable
@@ -1762,7 +1720,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
for i in 1..<n.len: for i in 1..<n.len:
var elem = semGenericParamInInvocation(c, n[i]) var elem = semGenericParamInInvocation(c, n[i])
addToResult(elem, true) addToResult(elem, true)
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) c.forwardTypeUpdates.add (result, n)
return return
elif t.kind != tyGenericBody: elif t.kind != tyGenericBody:
# we likely got code of the form TypeA[TypeB] where TypeA is # we likely got code of the form TypeA[TypeB] where TypeA is
@@ -1815,14 +1773,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
localError(c.config, n.info, errCannotInstantiateX % s.name.s) localError(c.config, n.info, errCannotInstantiateX % s.name.s)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam: elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam:
# isConcrete == false means this generic type is not instanciated here because # isConcrete == false means this generic type is not instanciated here because it invoked with generic parameters.
# it invoked with generic parameters. # Even if isConcrete == true, don't instanciate it now if there are any `tyForward` type params.
# Even if isConcrete == true, don't instanciate it now if there are # Such `tyForward` type params will be semchecked later and we can instanciate this next time.
# unresolved `tyForward` type params. # Some generic types like std/options.Option[T] needs a type kinds of the given type argument.
# Such `tyForward` type params will be semchecked later and we can
# instanciate this next time.
# Some generic types like std/options.Option[T] need the kind of the
# given type argument before their fields can be resolved.
# return `tyForward` instead of `tyGenericInvocation` because: # return `tyForward` instead of `tyGenericInvocation` because:
# ```nim # ```nim
@@ -1838,7 +1792,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
else: else:
assignType(result, newTypeS(tyForward, c)) assignType(result, newTypeS(tyForward, c))
result.sym = s result.sym = s
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) #fixes 1500 c.forwardTypeUpdates.add (result, n) #fixes 1500
return return
else: else:
result = instGenericContainer(c, n.info, result, result = instGenericContainer(c, n.info, result,
@@ -1879,38 +1833,6 @@ proc fixupTypeOf(c: PContext, prev: PType, typ: PType) =
if prev.kind != tyGenericBody: if prev.kind != tyGenericBody:
assignType(prev, result) assignType(prev, result)
proc decayTypeOfView(c: PContext, typ: PType): PType =
if typ == nil: return nil
let t = typ.skipTypes({tyGenericInst, tyAlias, tySink})
case t.kind
of tyVar, tyLent:
result = decayTypeOfView(c, t.elementType)
of tyTuple:
var changed = false
var kids = newSeq[PType](t.len)
for i in 0..<t.len:
kids[i] = decayTypeOfView(c, t[i])
if kids[i] != t[i]: changed = true
if changed:
result = copyType(t, c.idgen, t.owner)
for i in 0..<kids.len:
result[i] = kids[i]
if t.n != nil:
result.n = copyNode(t.n)
for it in t.n:
if it.kind == nkSym and it.sym.kind == skField:
let field = copySym(it.sym, c.idgen)
field.ast = it.sym.ast
if field.position >= 0 and field.position < kids.len:
field.typ = kids[field.position]
result.n.add newSymNode(field, it.info)
else:
result.n.add copyTree(it)
else:
result = typ
else:
result = typ
proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType = proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
var n = semExprWithType(c, n, {efDetermineType}) var n = semExprWithType(c, n, {efDetermineType})
if n.typ.kind == tyTypeDesc: if n.typ.kind == tyTypeDesc:
@@ -2110,7 +2032,6 @@ proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
result.incl tfNonConstExpr result.incl tfNonConstExpr
else: else:
result = base result = base
result = decayTypeOfView(c, result)
fixupTypeOf(c, prev, result) fixupTypeOf(c, prev, result)
proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
@@ -2136,7 +2057,6 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
result.incl tfNonConstExpr result.incl tfNonConstExpr
else: else:
result = base result = base
result = decayTypeOfView(c, result)
fixupTypeOf(c, prev, result) fixupTypeOf(c, prev, result)
proc semTypeIdent(c: PContext, n: PNode): PSym = proc semTypeIdent(c: PContext, n: PNode): PSym =
@@ -2414,7 +2334,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: else:
result = typeExpr.typ.base result = typeExpr.typ.base
if result.isMetaType and if result.isMetaType and
result.kind notin tyTypeClasses: result.kind != tyUserTypeClass:
# the dot expression may refer to a concept type in # the dot expression may refer to a concept type in
# a different module. allow a normal alias then. # a different module. allow a normal alias then.
let preprocessed = semGenericStmt(c, n) let preprocessed = semGenericStmt(c, n)

View File

@@ -15,8 +15,6 @@ import
magicsys, idents, lexer, options, parampatterns, trees, magicsys, idents, lexer, options, parampatterns, trees,
linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable linter, lineinfos, lowerings, modulegraphs, concepts, layeredtable
import typeallowed
import std/[intsets, strutils, tables] import std/[intsets, strutils, tables]
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
@@ -125,58 +123,6 @@ proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
result.calleeSym = nil result.calleeSym = nil
result.bindings = initLayeredTypeMap() result.bindings = initLayeredTypeMap()
proc materializeTupleViewType(t: PType; idgen: IdGenerator): PType =
case t.kind
of tyVar, tyLent:
result = materializeTupleViewType(t.elementType, idgen)
of tyTuple:
if classifyViewType(t) == noView:
result = t
else:
result = copyType(t, idgen, t.owner)
for i in 0..<t.len:
result[i] = materializeTupleViewType(t[i], idgen)
if t.n != nil:
result.n = copyNode(t.n)
for it in t.n:
if it.kind == nkSym and it.sym.kind == skField:
let field = copySym(it.sym, idgen)
field.ast = it.sym.ast
if field.position >= 0 and field.position < result.len:
field.typ = result[field.position]
result.n.add newSymNode(field, it.info)
else:
result.n.add copyTree(it)
else:
result = t
proc materializeTupleViewArg(c: PContext; targetType: PType; arg: PNode): PNode =
let targetTuple = targetType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
var tupleArg = arg
var prefix: PNode = nil
if targetTuple.len > 1 and arg.kind notin {nkHiddenAddr, nkSym}:
prefix = evalOnce(c.graph, arg, c.idgen, getCurrOwner(c))
tupleArg = prefix[^1]
let tupleConstr = newNodeIT(nkTupleConstr, arg.info, targetType)
for i in 0..<targetTuple.len:
let targetField = targetTuple[i]
var field = newTupleAccess(c.graph, tupleArg, i)
let sourceField = field.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
if sourceField.kind in {tyVar, tyLent}:
field = newDeref(field)
elif targetField.kind == tyTuple and classifyViewType(sourceField) != noView:
field = materializeTupleViewArg(c, targetField, field)
tupleConstr.add field
if prefix == nil:
result = tupleConstr
else:
result = newNodeIT(nkStmtListExpr, arg.info, targetType)
for i in 0..<(prefix.len - 1):
result.add prefix[i]
result.add tupleConstr
proc put(c: var TCandidate, key, val: PType) {.inline.} = proc put(c: var TCandidate, key, val: PType) {.inline.} =
## Given: proc foo[T](x: T); foo(4) ## Given: proc foo[T](x: T); foo(4)
## key: 'T' ## key: 'T'
@@ -189,12 +135,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
writeStackTrace() writeStackTrace()
if c.c.module.name.s == "temp3": if c.c.module.name.s == "temp3":
echo "binding ", key, " -> ", val echo "binding ", key, " -> ", val
put(c.bindings, key, val.skipIntLit(c.c.idgen))
let normalized = val.skipIntLit(c.c.idgen)
if normalized.kind == tyTuple and classifyViewType(normalized) != noView:
put(c.bindings, key, materializeTupleViewType(normalized, c.c.idgen))
else:
put(c.bindings, key, normalized)
proc typeRel*(c: var TCandidate, f, aOrig: PType, proc typeRel*(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation flags: TTypeRelFlags = {}): TTypeRelation
@@ -2259,16 +2200,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
result.add c.graph.emptyNode result.add c.graph.emptyNode
let targetTuple = result.typ.skipTypes({tyVar, tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred}) if arg.typ != nil and arg.typ.kind == tyLent:
let sourceTuple =
if arg.typ != nil:
arg.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct, tyInferred})
else:
nil
if sourceTuple != nil and sourceTuple.kind == tyTuple and targetTuple.kind == tyTuple and
classifyViewType(arg.typ) != noView and classifyViewType(result.typ) == noView:
result.add materializeTupleViewArg(c, targetTuple, arg)
elif arg.typ != nil and arg.typ.kind == tyLent:
let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType) let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType)
a.add arg a.add arg
result.add a result.add a
@@ -2902,11 +2834,9 @@ proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
else: break else: break
proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) = proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
template noMatch() = template noMatch() =
if m.calleeSym != nil and m.calleeSym.kind notin {skTemplate, skMacro}: c.mergeShadowScope #merge so that we don't have to resem for later overloads
c.mergeShadowScope
else:
c.closeShadowScope
m.state = csNoMatch m.state = csNoMatch
m.firstMismatch.arg = a m.firstMismatch.arg = a
m.firstMismatch.formal = formal m.firstMismatch.formal = formal

View File

@@ -118,24 +118,6 @@ proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite
le.flags.incl nfFirstWrite le.flags.incl nfFirstWrite
result[1] = ri result[1] = ri
proc resolveBorrowedRoutineSym(c: PTransf; s: PSym; info: TLineInfo): PSym =
# Follow borrow aliases to the underlying implementation symbol.
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
let body = getBody(c.graph, s)
if body.kind == nkSym:
result = body.sym
else:
result = nil
internalError(c.graph.config, info, "wrong AST for borrowed symbol")
proc transformSymAux(c: PTransf, n: PNode): PNode = proc transformSymAux(c: PTransf, n: PNode): PNode =
let s = n.sym let s = n.sym
if s.typ != nil and s.typ.callConv == ccClosure: if s.typ != nil and s.typ.callConv == ccClosure:
@@ -154,7 +136,17 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
var tc = c.transCon var tc = c.transCon
if sfBorrow in s.flags and s.kind in routineKinds: if sfBorrow in s.flags and s.kind in routineKinds:
# simply exchange the symbol: # 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: elif c.inlining > 0:
# see bug #13596: we use ref-based equality in the DFA for destruction # see bug #13596: we use ref-based equality in the DFA for destruction
# injections so we need to ensure unique nodes after iterator inlining # injections so we need to ensure unique nodes after iterator inlining
@@ -336,7 +328,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
if a.kind == nkSym: if a.kind == nkSym:
n[1] = transformSymAux(c, a) n[1] = transformSymAux(c, a)
return n return n
of nkLambdaKinds, nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects? of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
result = newTransNode(n) result = newTransNode(n)
let x = newSymNode(copySym(n[namePos].sym, c.idgen)) let x = newSymNode(copySym(n[namePos].sym, c.idgen))
c.transCon.mapping[n[namePos].sym.itemId] = x c.transCon.mapping[n[namePos].sym.itemId] = x
@@ -702,11 +694,6 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
of nkAddr, nkHiddenAddr: of nkAddr, nkHiddenAddr:
result = putArgInto(arg[0], formal) result = putArgInto(arg[0], formal)
if result == paViaIndirection: result = paFastAsgn if result == paViaIndirection: result = paFastAsgn
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if compareTypes(arg.typ, arg[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
result = putArgInto(arg[1], formal)
else:
result = paFastAsgn
of nkCurly, nkBracket: of nkCurly, nkBracket:
for i in 0..<arg.len: for i in 0..<arg.len:
if putArgInto(arg[i], formal) != paDirectMapping: if putArgInto(arg[i], formal) != paDirectMapping:
@@ -798,9 +785,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
discard c.breakSyms.pop discard c.breakSyms.pop
var iter = call[0].sym let iter = call[0].sym
if sfBorrow in iter.flags and iter.kind in routineKinds:
iter = resolveBorrowedRoutineSym(c, iter, n.info)
var v = newNodeI(nkVarSection, n.info) var v = newNodeI(nkVarSection, n.info)
for i in 0..<n.len - 2: for i in 0..<n.len - 2:

View File

@@ -16,11 +16,10 @@ const
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1 ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39" SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
NimonyStableCommit = "c189ef438598878b2f02f6a2ff91d08febafc04b" # unversioned \ NimonyStableCommit = "bbfb21529845567c55b67d176354daef0e7d6c29" # unversioned \
# Note that Nimony uses Nim as a git submodule but we don't want to install # 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 # Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive
# is **required** here. # is **required** here.
# Commit from 2026-04-27
# examples of possible values for fusion: #head, #ea82b54, 1.2.3 # examples of possible values for fusion: #head, #ea82b54, 1.2.3
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734" FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"

View File

@@ -1559,8 +1559,6 @@ macro expandMacros*(body: typed): untyped =
echo body.toStrLit echo body.toStrLit
result = body result = body
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
proc extractTypeImpl(n: NimNode): NimNode = proc extractTypeImpl(n: NimNode): NimNode =
## attempts to extract the type definition of the given symbol ## attempts to extract the type definition of the given symbol
case n.kind case n.kind
@@ -1575,17 +1573,11 @@ proc extractTypeImpl(n: NimNode): NimNode =
result = n[0].getImpl() result = n[0].getImpl()
of nnkTypeDef: of nnkTypeDef:
result = n[2] result = n[2]
if result.kind notin {nnkSym, nnkObjectTy, nnkRefTy, nnkPtrTy, nnkBracketExpr}:
# Handle typeof() and similar unresolvable type expressions
let typSym = if n[0].kind == nnkPragmaExpr: n[0][0] else: n[0]
if typSym.kind == nnkSym:
let resolved = typSym.getTypeInstSkipAlias()
if resolved.kind == nnkSym:
return resolved.getImpl.extractTypeImpl()
error("Invalid node to retrieve type implementation of: " & $result.kind)
else: error("Invalid node to retrieve type implementation of: " & $n.kind) else: error("Invalid node to retrieve type implementation of: " & $n.kind)
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
proc customPragmaNode(n: NimNode): NimNode = proc customPragmaNode(n: NimNode): NimNode =
result = nil result = nil
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr}) expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
@@ -1626,15 +1618,6 @@ proc customPragmaNode(n: NimNode): NimNode =
var typDef = getImpl(typInst) var typDef = getImpl(typInst)
while typDef != nil: while typDef != nil:
typDef.expectKind(nnkTypeDef) typDef.expectKind(nnkTypeDef)
# Resolve typeof() and similar unresolvable type expressions
if typDef[2].kind notin {nnkSym, nnkObjectTy, nnkRefTy, nnkPtrTy, nnkBracketExpr}:
let typSym = if typDef[0].kind == nnkPragmaExpr: typDef[0][0] else: typDef[0]
if typSym.kind == nnkSym:
let resolved = typSym.getTypeInstSkipAlias()
if resolved.kind == nnkSym:
typDef = getImpl(resolved)
continue
break
let typ = typDef[2].extractTypeImpl() let typ = typDef[2].extractTypeImpl()
if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break
let isRef = typ.kind in {nnkRefTy, nnkPtrTy} let isRef = typ.kind in {nnkRefTy, nnkPtrTy}

View File

@@ -9,11 +9,6 @@
when defined(js): when defined(js):
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".} {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
## .. warning:: NRE is deprecated.
## Use [Regex](https://github.com/nitely/nim-regex) or
## `NRE2 <nre2.html>`_ that wraps Regex so that you can easily replace NRE.
## PCRE library is now at end of life.
##
## What is NRE? ## What is NRE?
## ============ ## ============
## ##
@@ -89,7 +84,7 @@ type
Regex* = ref RegexDesc Regex* = ref RegexDesc
## Represents the pattern that things are matched against, constructed with ## Represents the pattern that things are matched against, constructed with
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo # ## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
## comment")` ## comment".`
## ##
## `pattern: string` ## `pattern: string`
## : the string that was used to create the pattern. For details on how ## : the string that was used to create the pattern. For details on how
@@ -159,7 +154,7 @@ type
## will need to pass these as separate flags to PCRE. ## will need to pass these as separate flags to PCRE.
RegexMatch* = object RegexMatch* = object
## Usually seen as `Option[RegexMatch]`, it represents the result of an ## Usually seen as Option[RegexMatch], it represents the result of an
## execution. On failure, it is none, on success, it is some. ## execution. On failure, it is none, on success, it is some.
## ##
## `pattern: Regex` ## `pattern: Regex`

View File

@@ -10,10 +10,6 @@
when defined(js): when defined(js):
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".} {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
## .. warning:: This module is deprecated.
## Use [Regex](https://github.com/nitely/nim-regex).
## PCRE library is now at end of life.
##
## Regular expression support for Nim. ## Regular expression support for Nim.
## ##
## This module is implemented by providing a wrapper around the ## This module is implemented by providing a wrapper around the

View File

@@ -128,7 +128,7 @@ proc getContentLength*(): string =
proc getContentType*(): string = proc getContentType*(): string =
## Returns contents of the `CONTENT_TYPE` environment variable. ## Returns contents of the `CONTENT_TYPE` environment variable.
return getEnv("CONTENT_TYPE") return getEnv("CONTENT_Type")
proc getDocumentRoot*(): string = proc getDocumentRoot*(): string =
## Returns contents of the `DOCUMENT_ROOT` environment variable. ## Returns contents of the `DOCUMENT_ROOT` environment variable.

View File

@@ -739,7 +739,7 @@ template withValue*[A, B](t: Table[A, B], key: A,
discard discard
iterator pairs*[A, B](t: Table[A, B]): (lent A, lent B) = iterator pairs*[A, B](t: Table[A, B]): (A, B) =
## Iterates over any `(key, value)` pair in the table `t`. ## Iterates over any `(key, value)` pair in the table `t`.
## ##
## See also: ## See also:
@@ -1201,7 +1201,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool =
iterator pairs*[A, B](t: TableRef[A, B]): (lent A, lent B) = iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
## Iterates over any `(key, value)` pair in the table `t`. ## Iterates over any `(key, value)` pair in the table `t`.
## ##
## See also: ## See also:
@@ -1789,7 +1789,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
iterator pairs*[A, B](t: OrderedTable[A, B]): (lent A, lent B) = iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
## Iterates over any `(key, value)` pair in the table `t` in insertion ## Iterates over any `(key, value)` pair in the table `t` in insertion
## order. ## order.
## ##
@@ -2212,7 +2212,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool =
iterator pairs*[A, B](t: OrderedTableRef[A, B]): (lent A, lent B) = iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
## Iterates over any `(key, value)` pair in the table `t` in insertion ## Iterates over any `(key, value)` pair in the table `t` in insertion
## order. ## order.
## ##
@@ -2622,7 +2622,7 @@ proc `==`*[A](s, t: CountTable[A]): bool =
equalsImpl(s, t) equalsImpl(s, t)
iterator pairs*[A](t: CountTable[A]): (lent A, int) = iterator pairs*[A](t: CountTable[A]): (A, int) =
## Iterates over any `(key, value)` pair in the table `t`. ## Iterates over any `(key, value)` pair in the table `t`.
## ##
## See also: ## See also:
@@ -2899,7 +2899,7 @@ proc `==`*[A](s, t: CountTableRef[A]): bool =
else: result = s[] == t[] else: result = s[] == t[]
iterator pairs*[A](t: CountTableRef[A]): (lent A, int) = iterator pairs*[A](t: CountTableRef[A]): (A, int) =
## Iterates over any `(key, value)` pair in the table `t`. ## Iterates over any `(key, value)` pair in the table `t`.
## ##
## See also: ## See also:

View File

@@ -588,9 +588,7 @@ proc handleShortOption(p: var OptParser; cmd: string) =
template next(): untyped = p.cmds[p.idx + 1] template next(): untyped = p.cmds[p.idx + 1]
let canTakeVal = card(p.shortNoVal) > 0 and let canTakeVal = card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal
p.key.len > 0 and p.key[0] notin p.shortNoVal
if i < cmd.len and cmd[i] in p.separators: if i < cmd.len and cmd[i] in p.separators:
# separator case # separator case
if prShortAllowSep in p.rules: if prShortAllowSep in p.rules:

View File

@@ -36,8 +36,6 @@ elif defined(linux):
# Android # Android
"/data/data/com.termux/files/usr/etc/tls/cert.pem", "/data/data/com.termux/files/usr/etc/tls/cert.pem",
"/system/etc/security/cacerts", "/system/etc/security/cacerts",
# Nix
"/etc/ssl/certs/ca-bundle.crt"
] ]
elif defined(bsd): elif defined(bsd):
const certificatePaths = [ const certificatePaths = [

View File

@@ -259,7 +259,7 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
result = s.readDataStrImpl(s, buffer, slice) result = s.readDataStrImpl(s, buffer, slice)
else: else:
# fallback # fallback
result = s.readData(beginStore(buffer, buffer.len, slice.a), slice.b + 1 - slice.a) result = s.readData(beginStore(buffer, slice.b + 1 - slice.a, slice.a), slice.b + 1 - slice.a)
endStore(buffer) endStore(buffer)
template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped = template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
@@ -1226,7 +1226,7 @@ else: # after 1.3 or JS not defined
jsOrVmBlock: jsOrVmBlock:
buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result] buffer[slice.a..<slice.a+result] = s.data[s.pos..<s.pos+result]
do: do:
copyMem(beginStore(buffer, buffer.len, slice.a), readRawData(s.data, s.pos), result) copyMem(beginStore(buffer, result, slice.a), readRawData(s.data, s.pos), result)
endStore(buffer) endStore(buffer)
inc(s.pos, result) inc(s.pos, result)
else: else:
@@ -1267,16 +1267,16 @@ else: # after 1.3 or JS not defined
var s = StringStream(s) var s = StringStream(s)
if bufLen <= 0: if bufLen <= 0:
return return
if s.pos + bufLen > s.data.len:
setLen(s.data, s.pos + bufLen)
when defined(js): when defined(js):
if s.pos + bufLen > s.data.len:
setLen(s.data, s.pos + bufLen)
try: try:
s.data[s.pos..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen] s.data[s.pos..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen]
except: except:
raise newException(Defect, "could not write to string stream, " & raise newException(Defect, "could not write to string stream, " &
"did you use a non-string buffer pointer?", getCurrentException()) "did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript): elif not defined(nimscript):
copyMem(beginStore(s.data, s.pos + bufLen, s.pos), buffer, bufLen) copyMem(beginStore(s.data, bufLen, s.pos), buffer, bufLen)
endStore(s.data) endStore(s.data)
inc(s.pos, bufLen) inc(s.pos, bufLen)
@@ -1346,7 +1346,7 @@ proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int =
proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int =
let len = slice.b + 1 - slice.a let len = slice.b + 1 - slice.a
result = readBuffer(FileStream(s).f, beginStore(buffer, buffer.len, slice.a), len) result = readBuffer(FileStream(s).f, beginStore(buffer, len, slice.a), len)
endStore(buffer) endStore(buffer)
proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int = proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int =

View File

@@ -18,12 +18,12 @@ proc addCstringN(result: var string, buf: cstring; buflen: int) =
# no nimvm support needed, so it doesn't need to be fast here either # no nimvm support needed, so it doesn't need to be fast here either
let oldLen = result.len let oldLen = result.len
let newLen = oldLen + buflen let newLen = oldLen + buflen
result.setLen newLen
{.cast(noSideEffect).}: {.cast(noSideEffect).}:
when declared(beginStore): when declared(completeStore):
c_memcpy(beginStore(result, newLen, oldLen), buf, buflen.csize_t) c_memcpy(beginStore(result, buflen, oldLen), buf, buflen.csize_t)
endStore(result) endStore(result)
else: else:
result.setLen newLen
discard c_memcpy(result[oldLen].addr, buf, buflen.csize_t) discard c_memcpy(result[oldLen].addr, buf, buflen.csize_t)
import std/private/[dragonbox, schubfach] import std/private/[dragonbox, schubfach]

View File

@@ -1,344 +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.
#
## What is NRE2?
## =============
##
## A regular expression library for Nim to replace deprecated NRE.
## It is implemented with `Regex<https://github.com/nitely/nim-regex>`_ ,
## that is pure Nim regex engine and guarantees linear time matching.
## It supports compiling regex and matching at compile-time and
## works with JS backend.
##
## NRE2 is mostly compatible with NRE and the syntax of regular expression is similar to PCRE.
## But it lacks a few features and how to set options in a pattern is different.
##
## The syntax of regular expression is explained in https://nitely.github.io/nim-regex/regex.html
runnableExamples:
import std/sugar
let vowels = re"[aeoui]"
let bounds = collect:
for match in "moiga".findIter(vowels): match.matchBounds
assert bounds == @[1 .. 1, 2 .. 2, 4 .. 4]
from std/sequtils import toSeq
let s = sequtils.toSeq("moiga".findIter(vowels))
# fully qualified to avoid confusion with nre.toSeq
assert s.len == 3
let firstVowel = "foo".find(vowels)
let hasVowel = firstVowel.isSome()
assert hasVowel
let matchBounds = firstVowel.get().captureBounds[-1]
assert matchBounds.a == 1
# as with module `re`, unless specified otherwise, `start` parameter in each
# proc indicates where the scan starts, but outputs are relative to the start
# of the input string, not to `start`:
assert find("uxabc", re"(?<=x|y)ab", start = 1).get.captures[-1] == "ab"
assert find("uxabc", re"ab", start = 3).isNone
import std/[options, tables]
import regex, regex/nfatype
export options
export regex.RegexFlags, regex.RegexError
type
Regex* = regex.Regex2
## Represents the pattern that things are matched against, constructed with
## `re(string)`. Examples: `re"foo"`, `re(r"(?x)foo #comment")`
##
## `captureCount: int`
## : the number of captures that the pattern has.
##
## `captureNameId: Table[string, int]`
## : a table from the capture names to their numeric id.
##
## The syntax of regular expression of Regex is explained in https://nitely.github.io/nim-regex/regex.html
RegexMatch* = object
## Usually seen as `Option[RegexMatch]`, it represents the result of an
## execution. On failure, it is none, on success, it is some.
##
## `str: string`
## : the string that was matched against
##
## `captures[]: string`
## : the string value of whatever was captured at that id. If the value
## is invalid, then behavior is undefined. If the id is `-1`, then
## the whole match is returned. If the given capture was not matched,
## `nil` is returned. See examples for `match`.
##
## `captureBounds[]: HSlice[int, int]`
## : gets the bounds of the given capture according to the same rules as
## the above. If the capture is not filled, then `None` is returned.
## The bounds are both inclusive. See examples for `match`.
##
## `match: string`
## : the full text of the match.
##
## `matchBounds: HSlice[int, int]`
## : the bounds of the match, as in `captureBounds[]`
##
## `(captureBounds|captures).toTable`
## : returns a table with each named capture as a key.
##
## `(captureBounds|captures).toSeq`
## : returns all the captures by their number.
##
## `$: string`
## : same as `match`
str*: string ## The string that was matched against.
matchImpl: regex.RegexMatch2
Captures* {.borrow: `.`.} = distinct RegexMatch
CaptureBounds* {.borrow: `.`.} = distinct RegexMatch
func captureCount*(pattern: Regex): int {.inline.} =
pattern.toRegex().groupsCount
func captureNameId*(pattern: Regex): Table[string, int] =
result = initTable[string, int](pattern.toRegex().namedGroups.len)
for k, v in pattern.toRegex().namedGroups:
result[k] = v
func captureBounds*(match: RegexMatch): CaptureBounds {.inline.} =
CaptureBounds(match)
func captures*(match: RegexMatch): Captures {.inline.} =
Captures(match)
func contains*(match: Captures or CaptureBounds, i: int): bool {.inline.} =
i >= -1 and i < match.matchImpl.groupsCount and match.matchImpl.group(i) != reNonCapture
func len*(match: Captures or CaptureBounds): int {.inline.} =
## Return the number of capturing groups
match.matchImpl.groupsCount
func `[]`*(match: CaptureBounds; i: int): HSlice[int, int] {.inline.} =
if i == -1: match.matchImpl.boundaries else: match.matchImpl.group(i)
func `[]`*(match: CaptureBounds; name: string): HSlice[int, int] {.inline.} =
result = match.matchImpl.group(name)
if result == reNonCapture:
raise newException(KeyError, "Group '" & name & "' was not captured")
func `[]`*(match: Captures; i: int): string {.inline.} =
match.str[CaptureBounds(match)[i]]
func `[]`*(match: Captures, name: string): string {.inline.} =
match.str[CaptureBounds(match)[name]]
func match*(match: RegexMatch): string {.inline.} =
match.str[match.matchImpl.boundaries]
func matchBounds*(match: RegexMatch): HSlice[int, int] {.inline.} =
match.matchImpl.boundaries
func contains*(match: CaptureBounds or Captures, name: string): bool {.inline.} =
name in match.matchImpl.namedGroups and
match.matchImpl.group(name) != reNonCapture
func toTable*(match: Captures): Table[string, string] =
result = initTable[string, string]()
for k, i in match.matchImpl.namedGroups:
let r = match.matchImpl.group(i)
if r != reNonCapture:
result[k] = match.str[r]
func toTable*(match: CaptureBounds): Table[string, HSlice[int, int]] =
result = initTable[string, HSlice[int, int]]()
for k, i in match.matchImpl.namedGroups:
let r = match.matchImpl.group(i)
if r != reNonCapture:
result[k] = match.matchImpl.group(i)
iterator items*(match: CaptureBounds; default = none(HSlice[int, int])): Option[HSlice[int, int]] =
for i in 0 ..< match.len:
yield if i in match: some(match[i]) else: default
iterator items*(match: Captures; default = none(string)): Option[string] =
for i in 0 ..< match.len:
yield if i in match: some(match[i]) else: default
func toSeq*(match: CaptureBounds;
default = none(HSlice[int, int])): seq[Option[HSlice[int, int]]] =
result = @[]
for it in match.items(default): result.add it
func toSeq*(match: Captures;
default: Option[string] = none(string)): seq[Option[string]] =
result = @[]
for it in match.items(default): result.add it
func `$`*(match: RegexMatch): string =
match.match
func re*(pattern: static string; flags: static RegexFlags = {}): static[Regex2] =
## Parse and compile a regular expression at compile-time
result = regex.re2(pattern, flags)
func re*(pattern: string; flags: RegexFlags = {}): Regex =
## Parse and compile a regular expression at run-time
result = regex.re2(pattern, flags)
func match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the
## string.
runnableExamples:
assert "foo".match(re"f").isSome
assert "foo".match(re"o").isNone
assert "abc".match(re"(\w)").get.captures[0] == "a"
assert "abc".match(re"(?P<letter>\w)").get.captures["letter"] == "a"
assert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
assert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
assert 0 in "abc".match(re"(\w)").get.captureBounds
assert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
assert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
var mat = default(RegexMatch)
let r = regex.startsWith(str.toOpenArray(0, min(str.high, endpos)), pattern, mat.matchImpl, start)
if r:
mat.str = str
some(mat)
else:
none(RegexMatch)
iterator findIter*(str: string; pattern: Regex; start = 0, endpos = int.high): RegexMatch =
## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every
## non-overlapping match:
runnableExamples:
import std/sugar
assert collect(for a in "2222".findIter(re"22"): a.match) == @["22", "22"]
# not @["22", "22", "22"]
## Arguments are the same as `find(...)<#find,string,Regex,int>`_
##
## Variants:
##
## - `proc findAll(...)` returns a `seq[string]`
var mat = RegexMatch(str: str)
# TODO:
# needs following PR to remove `substr` call.
# https://github.com/nitely/nim-regex/pull/162
for m in regex.findAll(str.substr(start, endpos), pattern):
mat.matchImpl = m
yield mat
proc find*(str: string; pattern: Regex; start = 0; endpos = int.high): Option[RegexMatch] =
## Finds the given pattern in the string between the end and start
## positions.
##
## `start`
## : The start point at which to start matching. `|abc` is `0`;
## `a|bc` is `1`
##
## `endpos`
## : The maximum index for a match; `int.high` means the end of the
## string, otherwise its an inclusive upper bound.
var mat = default(RegexMatch)
let r = regex.find(str.substr(start, endpos), pattern, mat.matchImpl)
# remove following code after regex.find get `start`/`last` parameter
for v in mat.matchImpl.captures.mitems:
v.a += start
v.b += start
mat.matchImpl.boundaries.a += start
mat.matchImpl.boundaries.b += start
if r:
mat.str = str
some(mat)
else:
none(RegexMatch)
proc findAll*(str: string; pattern: Regex; start = 0; endpos = int.high): seq[string] =
result = @[]
for match in str.findIter(pattern, start, endpos):
result.add(match.match)
proc contains*(str: string; pattern: Regex; start = 0; endpos = int.high): bool =
## Determine if the string contains the given pattern between the end and
## start positions:
## This function is equivalent to `isSome(str.find(pattern, start, endpos))`.
runnableExamples:
assert "abc".contains(re"bc")
assert not "abc".contains(re"cd")
assert not "abc".contains(re"a", start = 1)
isSome(str.find(pattern, start, endpos))
proc split*(str: string; pattern: Regex; maxSplit = -1; start = 0): seq[string] =
## Splits the string with the given regex. This works according to the
## rules that Perl and Javascript use.
##
## `start` behaves the same as in `find(...)<#find,string,Regex,int>`_.
##
runnableExamples:
# - If the match is zero-width, then the string is still split:
assert "123".split(re"") == @["1", "2", "3"]
# - If the pattern has a capture in it, it is added after the string
# split:
assert "12".split(re"(\d)") == @["", "1", "", "2", ""]
# - If `maxsplit != -1`, then the string will only be split
# `maxsplit - 1` times. This means that there will be `maxsplit`
# strings in the output seq.
assert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"]
result = splitIncl(str, pattern, maxSplit, start)
proc replace*(str: string; pattern: Regex;
subproc: proc (match: RegexMatch): string): string =
## Replaces each match of Regex in the string with `subproc`, which should
## never be or return `nil`.
##
## If `subproc` is a `proc (RegexMatch): string`, then it is executed with
## each match and the return value is the replacement value.
##
## If `subproc` is a `proc (string): string`, then it is executed with the
## full text of the match and the return value is the replacement value.
##
## If `subproc` is a string, the syntax is as follows:
##
## - `$$` - literal `$`
## - `$123` - capture number `123`
## - `$1$#` - first and second captures
## - `$#` - first capture
##
## Following syntax is not supported in NRE2
##
## - `$foo` - named capture `foo`
## - `${foo}` - same as above
## - `$0` - full match
##
## If a given capture is missing, `ValueError` is thrown.
proc by(m: RegexMatch2, s: string): string =
let mat = RegexMatch(str: s, matchImpl: m)
result = subproc(mat)
result = regex.replace(str, pattern, by)
proc replace*(str: string; pattern: Regex;
subproc: proc (match: string): string): string =
proc by(m: RegexMatch2; s: string): string =
result = subproc(s)
result = regex.replace(str, pattern, by)
proc replace*(str: string; pattern: Regex; sub: string): string =
result = regex.replace(str, pattern, sub)
func escapeRe*(str: string): string =
## Escapes the string so it doesn't match any special characters.
runnableExamples:
assert escapeRe("fly+wind") == "fly\\+wind"
assert escapeRe("nim*") == "nim\\*"
result = regex.escapeRe(str)

View File

@@ -1,14 +0,0 @@
import std/os
if getCommand() == "doc":
# std/nre2 requires nim-regex and it requires nim-unicodedb.
# when build documentation on CI, git clone them as nimble is not available
const PkgDir = "build/deps"
const Pkgs = ["nim-regex", "nim-unicodedb"]
for n in Pkgs:
if not dirExists(PkgDir / n):
exec("git clone -q https://github.com/nitely/" & n & " " & (PkgDir / n))
switch("path", "$nim" / PkgDir / n / "src")

View File

@@ -294,11 +294,7 @@ proc containsOrIncl*[A](s: var PackedSet[A], key: A): bool =
for i in 0..<s.elems: for i in 0..<s.elems:
if s.a[i] == ord(key): if s.a[i] == ord(key):
return true return true
if s.elems < s.a.len: incl(s, key)
s.a[s.elems] = ord(key)
inc(s.elems)
else:
incl(s, key)
result = false result = false
else: else:
var t = packedSetGet(s, ord(key) shr TrunkShift) var t = packedSetGet(s, ord(key) shr TrunkShift)

View File

@@ -84,7 +84,7 @@ func setSlice*(s: var string, slice: Slice[int]) =
when not declared(moveMem): when not declared(moveMem):
impl() impl()
else: else:
let p = beginStore(s, s.len) let p = beginStore(s, last - first + 1)
moveMem(p, addr p[first], last - first + 1) moveMem(p, addr p[first], last - first + 1)
endStore(s) endStore(s)
s.setLen(last - first + 1) s.setLen(last - first + 1)

View File

@@ -485,7 +485,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
while true: while true:
# fixes #9634; this pattern may need to be abstracted as a template if reused; # fixes #9634; this pattern may need to be abstracted as a template if reused;
# likely other io procs need this for correctness. # likely other io procs need this for correctness.
fgetsSuccess = c_fgets(cast[cstring](beginStore(line, pos + sp, pos)), sp.cint, f) != nil fgetsSuccess = c_fgets(cast[cstring](beginStore(line, sp, pos)), sp.cint, f) != nil
endStore(line) endStore(line)
if fgetsSuccess: break if fgetsSuccess: break
when not defined(nimscript): when not defined(nimscript):

View File

@@ -1703,8 +1703,7 @@ when not (notJSnotNims and defined(nimSeqsV2)):
# Needed so modules imported by system (e.g. syncio) can reference these without guards. # Needed so modules imported by system (e.g. syncio) can reference these without guards.
when notJSnotNims: when notJSnotNims:
# mm:refc: string = ptr NimStringDesc with data: UncheckedArray[char] # mm:refc: string = ptr NimStringDesc with data: UncheckedArray[char]
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
{.cast(noSideEffect).}: s.setLen(newLen)
let ns = cast[NimString](s) let ns = cast[NimString](s)
if ns == nil: nil if ns == nil: nil
else: cast[ptr UncheckedArray[char]](addr ns.data[start]) else: cast[ptr UncheckedArray[char]](addr ns.data[start])
@@ -1715,7 +1714,7 @@ when not (notJSnotNims and defined(nimSeqsV2)):
else: cast[ptr UncheckedArray[char]](addr ns.data[start]) else: cast[ptr UncheckedArray[char]](addr ns.data[start])
else: else:
# JS/nimscript: callers are guarded by whenNotVmJsNims/when not defined(js) # JS/nimscript: callers are guarded by whenNotVmJsNims/when not defined(js)
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = nil 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 proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = discard
template readRawData*(s: string; start = 0): ptr UncheckedArray[char] = nil template readRawData*(s: string; start = 0): ptr UncheckedArray[char] = nil
@@ -2419,33 +2418,6 @@ when notJSnotNims and hasAlloc:
when not defined(nimV2): when not defined(nimV2):
include "system/repr" include "system/repr"
func setLenUninit*(s: var string, newlen: Natural) {.nodestroy.} =
## Sets the length of string `s` to `newlen`.
## New slots will not be initialized.
##
## If the new length is smaller than the new length,
## `s` will be truncated.
let n = max(newLen, 0)
when nimvm:
s.setLen(n)
else:
when notJSnotNims:
when defined(nimSeqsV2):
{.noSideEffect.}:
let str = unsafeAddr s
when defined(nimsso):
setLengthStrV3Uninit(cast[ptr SmallString](str)[], newlen)
else:
setLengthStrV2Uninit(cast[ptr NimStringV2](str)[], newlen)
else:
{.noSideEffect.}:
when hasAlloc:
setLengthStrUninit(s, newlen)
else:
s.setLen(n)
else: s.setLen(n)
when notJSnotNims and hasThreadSupport and hostOS != "standalone": when notJSnotNims and hasThreadSupport and hostOS != "standalone":
when not defined(nimPreviewSlimSystem): when not defined(nimPreviewSlimSystem):
include "system/channels_builtin" include "system/channels_builtin"

View File

@@ -158,26 +158,6 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
s.p.data[newLen] = '\0' s.p.data[newLen] = '\0'
s.len = newLen s.len = newLen
proc setLengthStrV2Uninit(s: var NimStringV2, newLen: int) =
if newLen == 0:
discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
else:
if isLiteral(s):
let oldP = s.p
s.p = allocPayload(newLen)
s.p.cap = newLen
if s.len > 0:
copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
s.p.data[newLen] = '\0'
elif newLen > s.len:
let oldCap = s.p.cap and not strlitFlag
if newLen > oldCap:
let newCap = max(newLen, resize(oldCap))
s.p = reallocPayload0(s.p, oldCap, newCap)
s.p.cap = newCap
s.p.data[newLen] = '\0'
s.len = newLen
proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
if a.p == b.p and a.len == b.len: return if a.p == b.p and a.len == b.len: return
if isLiteral(b): if isLiteral(b):
@@ -236,17 +216,13 @@ func capacity*(self: string): int {.inline.} =
let str = cast[ptr NimStringV2](unsafeAddr self) let str = cast[ptr NimStringV2](unsafeAddr self)
result = if str.p != nil: str.p.cap and not strlitFlag else: 0 result = if str.p != nil: str.p.cap and not strlitFlag else: 0
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
## Sets s.len to `newLen` (new bytes are uninitialized), ensures unique ## Returns a writable pointer for bulk write of `ensuredLen` bytes starting at `start`.
## ownership, and returns a pointer to s[start] for bulk writing.
## Call `endStore(s)` afterwards for portability. ## Call `endStore(s)` afterwards for portability.
## To keep the current length, pass `s.len`. {.cast(noSideEffect).}: prepareMutation(s)
{.cast(noSideEffect).}: let str = cast[ptr NimStringV2](unsafeAddr s)
let p = cast[ptr NimStringV2](addr s) if str.p == nil: nil
setLengthStrV2Uninit(p[], newLen) else: cast[ptr UncheckedArray[char]](addr str.p.data[start])
prepareMutation(s)
if p.p == nil: nil
else: cast[ptr UncheckedArray[char]](addr p.p.data[start])
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
## No-op for non-SSO strings; call after bulk writes via `beginStore`. ## No-op for non-SSO strings; call after bulk writes via `beginStore`.

View File

@@ -496,75 +496,28 @@ proc mnewString(len: int): SmallString {.compilerproc.} =
result.more = p result.more = p
setSSLen(result, HeapSlen) setSSLen(result, HeapSlen)
proc setLengthStr(s: var SmallString; newLen: int; zeroing: bool) = proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} =
# Shared implementation for setLengthStrV2 (zeroing) and setLengthStrV3Uninit ## Sets the length of s to newLen, zeroing new bytes on growth.
# Difference between the two modes:
# - inline/medium -> long growth: alloc0 (zeroing) vs alloc (uninit)
# - long -> long growth: zeroMem the new tail (zeroing) or skip it (uninit)
let slen = ssLen(s) let slen = ssLen(s)
let curLen = if slen > PayloadSize: s.more.fullLen else: slen let curLen = if slen > PayloadSize: s.more.fullLen else: slen
if newLen == curLen: return if newLen == curLen: return
if newLen < curLen: if newLen <= 0:
# Shrinking:
if slen > PayloadSize: if slen > PayloadSize:
if slen == HeapSlen and s.more.rc == 1: if slen == HeapSlen and s.more.rc == 1:
# Unique heap block: keep the buffer allocated to avoid alloc/dealloc s.more.fullLen = 0
# ping-pong when callers shrink then grow (e.g. setLen(0) + add loops). s.more.data[0] = '\0'
s.more.fullLen = newLen
s.more.data[newLen] = '\0'
else: else:
# shared or static block: detach and go back to inline # shared or static block: detach and go back to empty inline
if newLen <= 0: nimDestroyStrV1(s)
nimDestroyStrV1(s) s.bytes = 0 # slen=0, all inline chars zeroed
s.bytes = 0
else:
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)
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: else:
# inline/medium shrink s.bytes = 0 # slen=0, all inline chars zeroed (SWAR safe)
if newLen <= 0:
s.bytes = 0
else:
let inl = inlinePtr(s)
inl[newLen] = '\0'
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)
return return
if slen <= PayloadSize: if slen <= PayloadSize:
if newLen <= PayloadSize: if newLen <= PayloadSize:
let inl = inlinePtr(s) let inl = inlinePtr(s)
if newLen > curLen: if newLen > curLen:
# Grow within inline/medium zeroMem(addr inl[curLen], newLen - curLen)
# Bytes above newLen already zero by the SWAR invariant,
# so setSSLen is sufficient.
if zeroing:
zeroMem(addr inl[curLen], newLen - curLen)
inl[newLen] = '\0' inl[newLen] = '\0'
setSSLen(s, newLen) setSSLen(s, newLen)
else: else:
@@ -589,33 +542,43 @@ proc setLengthStr(s: var SmallString; newLen: int; zeroing: bool) =
else: else:
# grow into long # grow into long
let newCap = resize(newLen) let newCap = resize(newLen)
let p = if zeroing: let p = cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
# bytes [curLen..newLen] and p.data[newLen] zeroed by alloc0
cast[ptr LongString](alloc0(LongStringDataOffset + newCap + 1))
else:
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
p.data[newLen] = '\0'
p
p.rc = 1 p.rc = 1
p.fullLen = newLen p.fullLen = newLen
p.capImpl = newCap p.capImpl = newCap
copyMem(addr p.data[0], inlinePtr(s), curLen) copyMem(addr p.data[0], inlinePtr(s), curLen)
# bytes [curLen..newLen] zeroed by alloc0; p.data[newLen] = '\0' by alloc0
s.more = p s.more = p
setSSLen(s, HeapSlen) setSSLen(s, HeapSlen)
else: else:
# currently long: grow within the heap buffer (shrinking already returned above) # currently long
ensureUniqueLong(s, curLen, newLen) # sets fullLen = newLen if newLen <= PayloadSize:
if zeroing and newLen > curLen: # shrink back to inline
zeroMem(addr s.more.data[curLen], newLen - curLen) let old = s.more
s.more.data[newLen] = '\0' let inl = inlinePtr(s)
copyMem(inl, addr old.data[0], newLen)
proc setLengthStrV2(s: var SmallString; newLen: int) {.compilerRtl.} = inl[newLen] = '\0'
## Sets the length of `s` to `newLen`, zeroing new bytes on growth. if slen == HeapSlen and atomicSubFetch(old.rc, 1) == 0:
setLengthStr(s, newLen, zeroing = true) dealloc(old)
# Zero padding bytes in `bytes` for SWAR invariant
proc setLengthStrV3Uninit(s: var SmallString; newLen: int) {.compilerRtl.} = if newLen < AlwaysAvail:
## Sets the length of `s` to `newLen`, NOT zeroing new bytes on growth. when system.cpuEndian == littleEndian:
setLengthStr(s, newLen, zeroing = false) 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.} = proc nimAsgnStrV2(a: var SmallString; b: SmallString) {.compilerRtl, inline.} =
if ssLen(b) <= PayloadSize: if ssLen(b) <= PayloadSize:
@@ -721,37 +684,18 @@ proc completeStore(s: var SmallString) {.compilerproc, inline.} =
proc completeStore*(s: var string) {.inline.} = proc completeStore*(s: var string) {.inline.} =
completeStore(cast[ptr SmallString](addr s)[]) completeStore(cast[ptr SmallString](addr s)[])
proc beginStore*(s: var string; newLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} = proc beginStore*(s: var string; ensuredLen: int; start = 0): ptr UncheckedArray[char] {.inline, noSideEffect, raises: [], tags: [].} =
## Sets s.len to `newLen` (new bytes are uninitialized), ensures unique ## Prepares `s` for a bulk write of `ensuredLen` bytes starting at `start`.
## ownership, and returns a pointer to s[start] for bulk writing. ## The caller must ensure `s.len >= start + ensuredLen` (e.g. via `newString` or `setLen`).
## Call `endStore(s)` afterwards to sync the inline cache. ## Call `endStore(s)` afterwards to sync the inline cache.
## To keep the current length, pass `s.len`.
{.cast(noSideEffect).}: {.cast(noSideEffect).}:
let ss = cast[ptr SmallString](addr s) let ss = cast[ptr SmallString](addr s)
let slen = ssLen(ss[]) let slen = ssLen(ss[])
let curLen = if slen > PayloadSize: ss[].more.fullLen else: slen if slen > PayloadSize:
if newLen <= PayloadSize and slen <= PayloadSize: ensureUniqueLong(ss[], ss[].more.fullLen, ss[].more.fullLen)
# Stay inline/medium.
if newLen != curLen:
setSSLen(ss[], newLen)
result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start))
elif slen <= PayloadSize:
# Inline/medium → long.
let newCap = resize(newLen)
let p = cast[ptr LongString](alloc(LongStringDataOffset + newCap + 1))
p.rc = 1
p.fullLen = newLen
p.capImpl = newCap
copyMem(addr p.data[0], inlinePtr(ss[]), curLen)
p.data[newLen] = '\0'
ss[].more = p
setSSLen(ss[], HeapSlen)
result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start]) result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
else: else:
# Already long: resize within heap (no transition back to inline). result = cast[ptr UncheckedArray[char]](cast[uint](inlinePtr(ss[])) + uint(start))
ensureUniqueLong(ss[], curLen, newLen)
ss[].more.data[newLen] = '\0'
result = cast[ptr UncheckedArray[char]](addr ss[].more.data[start])
proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} = proc endStore*(s: var string) {.inline, noSideEffect, raises: [], tags: [].} =
## Syncs the inline cache after bulk writes via `beginStore`. No-op for short/medium strings. ## Syncs the inline cache after bulk writes via `beginStore`. No-op for short/medium strings.

View File

@@ -244,31 +244,6 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
result.len = n result.len = n
result.data[n] = '\0' result.data[n] = '\0'
proc setLengthStrUninit(s: var string, newlen: Natural) {.nodestroy.} =
## Sets the `s` length to `newlen` without zeroing memory on growth.
## Terminating zero for cstring compatibility is set.
var str = cast[NimString](s)
let n = max(newLen, 0)
if str == nil:
if n == 0: return
else:
str = rawNewStringNoInit(n)
str.data[n] = '\0'
str.len = n
s = cast[string](str)
else:
if n > str.space:
let sp = max(resize(str.space), n)
str = rawNewStringNoInit(sp)
copyMem(addr str.data[0], unsafeAddr s[0], s.len)
str.data[n] = '\0'
str.len = n
s = cast[string](str)
elif n != s.len:
str.data[n] = '\0'
str.len = n
else: return
# ----------------- sequences ---------------------------------------------- # ----------------- sequences ----------------------------------------------
proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} = proc incrSeq(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compilerproc.} =
@@ -299,22 +274,12 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} =
# since we steal the content from 's', it's crucial to set s's len to 0. # since we steal the content from 's', it's crucial to set s's len to 0.
s.len = 0 s.len = 0
proc newSeqUninitRaw(typ: PNimType; len: int): pointer {.inline.} =
## Creates a sequence payload with capacity and length `len` without
## forcing zero-initialization for `ntfNoRefs` element types.
result = nimNewSeqOfCap(typ, len)
cast[PGenericSeq](result).len = len
proc extendCapacityRaw(src: PGenericSeq; typ: PNimType; proc extendCapacityRaw(src: PGenericSeq; typ: PNimType;
elemSize, elemAlign, newLen: int; elemSize, elemAlign, newLen: int): PGenericSeq {.inline.} =
doInit: static bool): PGenericSeq {.inline.} =
## Reallocs `src` to fit `newLen` elements without any checks. ## Reallocs `src` to fit `newLen` elements without any checks.
## Capacity always increases to at least next `resize` step. ## Capacity always increases to at least next `resize` step.
let newCap = max(resize(src.space), newLen) let newCap = max(resize(src.space), newLen)
when doInit: result = cast[PGenericSeq](newSeq(typ, newCap))
result = cast[PGenericSeq](newSeq(typ, newCap))
else:
result = cast[PGenericSeq](newSeqUninitRaw(typ, newCap))
copyMem(dataPointer(result, elemAlign), dataPointer(src, elemAlign), src.len * elemSize) copyMem(dataPointer(result, elemAlign), dataPointer(src, elemAlign), src.len * elemSize)
# since we steal the content from 's', it's crucial to set s's len to 0. # since we steal the content from 's', it's crucial to set s's len to 0.
src.len = 0 src.len = 0
@@ -345,19 +310,15 @@ proc truncateRaw(src: PGenericSeq; baseFlags: set[TNimTypeFlag]; isTrivial: bool
((result.len-%newLen) *% elemSize)) ((result.len-%newLen) *% elemSize))
template setLengthSeqImpl(s: PGenericSeq, typ: PNimType, newLen: int; isTrivial: bool; template setLengthSeqImpl(s: PGenericSeq, typ: PNimType, newLen: int; isTrivial: bool;
doInit: static bool) = doInit: static bool) =
if s == nil: if s == nil:
if newLen == 0: return s if newLen == 0: return s
else: else: return cast[PGenericSeq](newSeq(typ, newLen)) # newSeq zeroes!
when doInit:
return cast[PGenericSeq](newSeq(typ, newLen)) # newSeq zeroes!
else:
return cast[PGenericSeq](newSeqUninitRaw(typ, newLen))
else: else:
let elemSize = typ.base.size let elemSize = typ.base.size
let elemAlign = typ.base.align let elemAlign = typ.base.align
result = if newLen > s.space: result = if newLen > s.space:
s.extendCapacityRaw(typ, elemSize, elemAlign, newLen, doInit) s.extendCapacityRaw(typ, elemSize, elemAlign, newLen)
elif newLen < s.len: elif newLen < s.len:
s.truncateRaw(typ.base.flags, isTrivial, elemSize, elemAlign, newLen) s.truncateRaw(typ.base.flags, isTrivial, elemSize, elemAlign, newLen)
else: else:

View File

@@ -39,7 +39,7 @@ architecture combinations:
|--------------------------------|----------------------------------------| |--------------------------------|----------------------------------------|
| Windows (Windows XP or greater) | x86 and x86_64 | | Windows (Windows XP or greater) | x86 and x86_64 |
| Linux (most distributions) | x86, x86_64, ppc64, and armv6l | | Linux (most distributions) | x86, x86_64, ppc64, and armv6l |
| Mac OS X (10.4 or greater) | x86, x86_64, ppc64, and Apple Silicon (ARM64) | | Mac OS X (10.04 or greater) | x86, x86_64, ppc64, and Apple Silicon (ARM64) |
More platforms are supported, however, they are not tested regularly and they More platforms are supported, however, they are not tested regularly and they
may not be as stable as the above-listed platforms. may not be as stable as the above-listed platforms.

View File

@@ -1,20 +0,0 @@
discard """
matrix: "--mm:orc"
output: '''
found entry
'''
"""
import std/tables
type NoCopies = object
proc `=copy`(a: var NoCopies, b: NoCopies) {.error.}
# bug #24720
proc foo() =
var t: Table[int, NoCopies]
t[3] = NoCopies() # only moves
for k, v in t.pairs(): # lent values, no need to copy!
echo "found entry"
foo()

View File

@@ -1,33 +0,0 @@
discard """
cmd: '''nim c --mm:arc --expandArc:foo $file'''
nimout: '''
--expandArc: foo
var broken_cursor
block :tmp:
var i
var i_1 = 0
let L = len(seq[Large](broken_cursor))
block :tmp_1:
while i_1 < L:
i = seq[Large](broken_cursor)[i_1]
discard i
{.push, overflowChecks: false.}
inc(i_1, 1)
{.pop.}
-- end of expandArc ------------------------
'''
"""
type
Large = array[1024, byte]
List = distinct seq[Large]
proc foo =
var
broken: List
for i in seq[Large](broken):
discard i
foo()

View File

@@ -1,45 +0,0 @@
discard """
output: '''
246
246
'''
"""
# issue #25730
type
Inner[T] = object
x: T
Foo[T] = object
inner: Inner[T]
Bar[T] = object
foo: Foo[T]
proc `=sink`[T](a: var Inner[T], b: Inner[T]) {.nodestroy.} =
a.x = b.x * 2
proc `=copy`[T](a: var Inner[T], b: Inner[T]) {.nodestroy.} =
a.x = b.x * 2
when true:
proc `=sink`[T](a: var Bar[T], b: Bar[T]) {.nodestroy.} =
`=sink`(a.foo, b.foo)
proc `=copy`[T](a: var Bar[T], b: Bar[T]) {.nodestroy.} =
`=copy`(a.foo, b.foo)
proc useSink() =
let a = Bar[int](foo: Foo[int](inner: Inner[int](x: 123)))
var b: Bar[int]
`=sink`(b, a)
echo b.foo.inner.x
useSink()
proc useCopy() =
let a = Bar[int](foo: Foo[int](inner: Inner[int](x: 123)))
var b: Bar[int]
`=copy`(b, a)
echo b.foo.inner.x
useCopy()

View File

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

View File

@@ -1,15 +0,0 @@
discard """
cmd: "nim check --hints:off $file"
action: "reject"
nimout: '''
t25732.nim(15, 32) Error: undeclared identifier: 'a'
t25732.nim(15, 32) Error: expression 'a' has no type (or is ambiguous)
t25732.nim(15, 33) Error: undeclared field: 'b'
t25732.nim(15, 33) Error: undeclared field: '.'
t25732.nim(15, 33) Error: undeclared field: '.'
'''
"""
static: (for f in [0]: discard a.b == f)

View File

@@ -7,10 +7,8 @@ discard """
1.0 1.0
2.0 2.0
55 55
@[1, 2]
''' '''
""" """
import std/strbasics
# Object variant / case object # Object variant / case object
type type
@@ -81,14 +79,3 @@ let x = compute:
echo x echo x
# Crash: bridge.nim(206, 5) `allowEmpty` unexpected nkEmpty [AssertionDefect]
# Bare closure iterator type alias
type IntIter = iterator(): int {.closure.}
proc run(it: IntIter): seq[int] =
result = @[]
for x in it():
result.add(x)
let gen: IntIter = iterator(): int {.closure.} =
yield 1
yield 2
echo run(gen)

View File

@@ -457,12 +457,3 @@ let runes1 = buggyVersion("en") # <-- CRASHES HERE
doAssert runes1.len == runes2.len doAssert runes1.len == runes2.len
# echo "Got ", runes1.len, " runes" # echo "Got ", runes1.len, " runes"
block: # bug #25724
iterator c(): int =
when nimvm: yield 0
else: yield 1
for w in c():
let n = w
(proc() = discard n)()

View File

@@ -4,12 +4,7 @@ js 3.14
7 7
1 1
-21550 -21550
-21550 -21550'''
none(TT)
()
destroyed
destroyed
'''
""" """
# This file tests the JavaScript generator # This file tests the JavaScript generator
@@ -61,15 +56,3 @@ proc foo09() =
const y = 86400 const y = 86400
echo (x - (y - 1)) div y # Still gives `-21551` echo (x - (y - 1)) div y # Still gives `-21551`
foo09() foo09()
import std/options
type TT = object
proc `=destroy`(x: TT) = echo "destroyed"
func test1: Option[TT] = discard
func test2: TT = discard
echo test1() # Crash in JS backend, not crash in C backend
echo test2() # Not crash

View File

@@ -1,8 +0,0 @@
type
A* = object
discard
B* = object
discard
C* = A | B

View File

@@ -1,22 +0,0 @@
discard """
action: "compile"
"""
import deps/cisaorb
when true:
# These work fine.
discard default(cisaorb.A)
proc f1(x: cisaorb.A) = discard
discard default(cisaorb.B)
proc f2(x: cisaorb.B) = discard
discard default(A)
proc f3(x: A) = discard
discard default(B)
proc f4(x: B) = discard
proc f5(x: C) = discard
proc f6(x: cisaorb.C | C) = discard
proc doesWork(x: A | B) = discard
# Doesn't compile.
proc f(x: cisaorb.C) = discard

View File

@@ -31,9 +31,6 @@ cmdShortOption key: v value: ''
cmdArgument key: ABC value: '' cmdArgument key: ABC value: ''
cmdShortOption key: j value: '4' cmdShortOption key: j value: '4'
cmdArgument key: ok value: '' cmdArgument key: ok value: ''
parseopt stdin
cmdShortOption key: j value: '4'
cmdShortOption key: value: ''
''' '''
joinable: false joinable: false
""" """
@@ -157,9 +154,3 @@ arg 6 ai.len:4 :{a7'b}"""
var n = parseopt.initOptParser("-j4 ok", shortnoVal = {'n'}, longnoVal = @["novalue"]) var n = parseopt.initOptParser("-j4 ok", shortnoVal = {'n'}, longnoVal = @["novalue"])
for kind, key, val in parseopt.getopt(n): for kind, key, val in parseopt.getopt(n):
echo kind," key: ", key, " value: '", val, "'" echo kind," key: ", key, " value: '", val, "'"
block: # fix #25738
echo "parseopt stdin"
var p = parseopt.initOptParser("-j4 -", shortNoVal = {'n'})
for kind, key, val in parseopt.getopt(p):
echo kind," key: ", key, " value: '", val, "'"

View File

@@ -1,87 +0,0 @@
# issue #25627
import std/tables
type
FsoKind = enum
fsoFile
fsoDir
fsoLink
FakeFso = ref object
kind: FsoKind
dirName: string
files: OrderedTable[string, FakeFso]
DirStruct = object
root = FakeFso(kind: fsoDir, dirName: "/")
let dir = DirStruct()
doAssert dir.root.kind == fsoDir
doAssert dir.root.dirName == "/"
doAssert dir.root.files.len == 0
block:
type
Opt[T] = object
when T is ref:
val: T
x: int
else:
val: T
x: string
DefaultOpt = ref object
files: Opt[DefaultOpt]
OptDirStruct = object
root = DefaultOpt()
let dir = OptDirStruct()
doAssert dir.root.files.x is int
block:
type
Opt[T] = object
when T is ref:
x: int
else:
x: string
Foo[T] = object
x: Opt[T]
Nested = ref object
files: Foo[Nested]
let nested = Nested()
doAssert nested.files.x.x is int
block:
type
Foo[T] = object
x = sizeof(T)
Sized = ref object
files: Foo[Sized]
let sized = Sized()
doAssert sized.files.x == sizeof(Sized)
block:
type
Generic[T] = object
t: T
WindowObj = object
svgCache: Generic[SVGSVGElement]
SVGSVGElement = Generic[SVGSVGElementObj]
SVGSVGElementObj = object
proc foo() =
let p: pointer = nil
discard cast[ptr WindowObj](p)
foo()

View File

@@ -1,34 +0,0 @@
proc temp(one: int, two: int, three: int) =
discard
template temp(body: untyped): untyped =
body
temp:
proc a(tp: int) =
discard
proc mixedTemp(x: int) =
discard
proc mixedTemp(x: bool) =
discard
template mixedTemp(body: untyped): untyped =
body
# The `bool` proc should win here so `xx` survives
mixedTemp (let xx = 1; true)
discard xx
proc sinkTemp(x: int) =
discard
template sinkTemp(body: untyped): untyped =
discard
# Here the template should win here so `let xy` is sunk into template as AST
sinkTemp (let xy = "template"; xy)
when declared(xy):
{.error: "xy leaked from failed proc candidate".}

View File

@@ -549,20 +549,3 @@ block:
type X {.p.} = object type X {.p.} = object
doAssert foo(X()) doAssert foo(X())
block: # typeof() type alias preserves field pragmas
template myFieldPragma {.pragma.}
type Orig = object
x {.myFieldPragma.}: int
var orig: Orig
# Direct typeof alias
type TAlias = typeof(orig)
var a: TAlias
doAssert a.x.hasCustomPragma(myFieldPragma)
# Indirect alias of typeof alias
type TAlias2 = TAlias
var b: TAlias2
doAssert b.x.hasCustomPragma(myFieldPragma)

View File

@@ -1,196 +0,0 @@
import std/[assertions, options, sequtils, strutils, tables]
import std/nre2
block:
let pattern = "[0-9"
doAssertRaises(RegexError): discard re(pattern)
block: # captures
block: # capture bounds are correct
let ex1 = re("([0-9])")
doAssert "1 23".find(ex1).get.matchBounds == 0 .. 0
doAssert "1 23".find(ex1).get.captureBounds[0] == 0 .. 0
doAssert "1 23".find(ex1, 1).get.matchBounds == 2 .. 2
doAssert "1 23".find(ex1, 3).get.matchBounds == 3 .. 3
let ex2 = re("()()()()()()()()()()([0-9])")
doAssert "824".find(ex2).get.captureBounds[0] == 0 .. -1
doAssert "824".find(ex2).get.captureBounds[10] == 0 .. 0
let ex3 = re("([0-9]+)")
doAssert "824".find(ex3).get.captureBounds[0] == 0 .. 2
block: # named captures
let ex1 = "foobar".find(re("(?P<foo>foo)(?P<bar>bar)"))
doAssert ex1.get.captures["foo"] == "foo"
doAssert ex1.get.captures["bar"] == "bar"
let ex2 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
doAssert "foo" in ex2.get.captureBounds
doAssert ex2.get.captures["foo"] == "foo"
doAssert not ("bar" in ex2.get.captures)
doAssertRaises(KeyError):
discard ex2.get.captures["bar"]
block: # named capture bounds
let ex1 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
doAssert "foo" in ex1.get.captureBounds
doAssert ex1.get.captureBounds["foo"] == 0..2
doAssert not ("bar" in ex1.get.captures)
doAssertRaises(KeyError):
discard ex1.get.captureBounds["bar"]
block: # capture count
let ex1 = re("(?P<foo>foo)(?P<bar>bar)?")
doAssert ex1.captureCount == 2
doAssert ex1.captureNameId == {"foo" : 0, "bar" : 1}.toTable()
block: # named capture table
let ex1 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
doAssert ex1.get.captures.toTable == {"foo" : "foo"}.toTable()
doAssert ex1.get.captureBounds.toTable == {"foo" : 0..2}.toTable()
let ex2 = "foobar".find(re("(?P<foo>foo)(?P<bar>bar)?"))
doAssert ex2.get.captures.toTable == {"foo" : "foo", "bar" : "bar"}.toTable()
block: # capture sequence
let ex1 = "foo".find(re("(?P<foo>foo)(?P<bar>bar)?"))
doAssert ex1.get.captures.toSeq == @[some("foo"), none(string)]
doAssert ex1.get.captureBounds.toSeq == @[some(0..2), none(Slice[int])]
doAssert ex1.get.captures.toSeq(some("")) == @[some("foo"), some("")]
let ex2 = "foobar".find(re("(?P<foo>foo)(?P<bar>bar)?"))
doAssert ex2.get.captures.toSeq == @[some("foo"), some("bar")]
block: # match
block: # upper bound must be inclusive
doAssert "abc".match(re"abc", endpos = -1) == none(RegexMatch)
doAssert "abc".match(re"abc", endpos = 1) == none(RegexMatch)
doAssert "abc".match(re"abc", endpos = 2) != none(RegexMatch)
block: # match examples
doAssert "abc".match(re"(\w)").get.captures[0] == "a"
doAssert "abc".match(re"(?P<letter>\w)").get.captures["letter"] == "a"
doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab"
doAssert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0
doAssert "abc".match(re"").get.captureBounds[-1] == 0 .. -1
doAssert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2
let cap1 = "abc".match(re"(\w)(\w)+").get.captures
doAssert cap1.len == 2
doAssert 0 in cap1
doAssert 1 in cap1
doAssert cap1[0] == "a" and cap1[1] == "c"
doAssert 0 in "abc".match(re"(\w)+").get.captureBounds
block: # match test cases
doAssert "123".match(re"").get.matchBounds == 0 .. -1
let mat1 = "123".match(re"123").get
doAssert mat1.matchBounds == 0 .. 2
doAssert mat1.match == "123"
block: # find
block: # find text
doAssert "3213a".find(re"[a-z]").get.match == "a"
doAssert sequtils.toSeq(findIter("1 2 3 4 5 6 7 8 ", re" ")).mapIt(
it.match
) == @[" ", " ", " ", " ", " ", " ", " ", " "]
block: # find bounds
doAssert sequtils.toSeq(findIter("1 2 3 4 5 ", re" ")).mapIt(
it.matchBounds
) == @[1..1, 3..3, 5..5, 7..7, 9..9]
block: # overlapping find
doAssert "222".findAll(re"22") == @["22"]
doAssert "2222".findAll(re"22") == @["22", "22"]
block: # len 0 find
doAssert "".findAll(re"\ ") == newSeq[string]()
doAssert "".findAll(re"") == @[""]
doAssert "abc".findAll(re"") == @["", "", "", ""]
doAssert "word word".findAll(re"\b") == @["", "", "", ""]
doAssert "word\r\lword".findAll(re"(?m)$") == @["", ""]
doAssert "слово слово".findAll(re"\b") == @["", "", "", ""]
block: # contains
doAssert "abc".contains(re"bc")
doAssert not "abc".contains(re"cd")
doAssert not "abc".contains(re"a", start = 1)
block: # string splitting
block: # splitting strings
doAssert "1 2 3 4 5 6 ".split(re" ") == @["1", "2", "3", "4", "5", "6", ""]
doAssert "1 2 ".split(re(" ")) == @["1", "", "2", "", ""]
doAssert "1 2".split(re(" ")) == @["1", "2"]
doAssert "foo".split(re("foo")) == @["", ""]
doAssert "".split(re"foo") == @[""]
doAssert "9".split(re"\son\s") == @["9"]
block: # captured patterns
doAssert "12".split(re"(\d)") == @["", "1", "", "2", ""]
block: # maxsplit
doAssert "123".split(re"", maxsplit = 2) == @["1", "23"]
doAssert "123".split(re"", maxsplit = 1) == @["123"]
doAssert "123".split(re"", maxsplit = -1) == @["1", "2", "3"]
doAssert "1 2 3".split(re" ", maxsplit = 1) == @["1 2 3"]
doAssert "1 2 3".split(re" ", maxsplit = 2) == @["1", "2 3"]
doAssert "1 2 3".split(re"( )", maxsplit = 2) == @["1", " ", "2 3"]
block: # split with 0-length match
doAssert "12345".split(re("")) == @["1", "2", "3", "4", "5"]
doAssert "".split(re"") == newSeq[string]()
doAssert "word word".split(re"\b") == @["word", " ", "word"]
#doAssert "word\r\lword".split(re"(?m)$") == @["word", "\r\lword"]
doAssert "слово слово".split(re"(\b)") == @["слово", "", " ", "", "слово", ""]
block: # perl split tests
doAssert "forty-two" .split(re"") .join(",") == "f,o,r,t,y,-,t,w,o"
doAssert "forty-two" .split(re"", 3) .join(",") == "f,o,rty-two"
doAssert "split this string" .split(re" ") .join(",") == "split,this,string"
doAssert "split this string" .split(re" ", 2) .join(",") == "split,this string"
doAssert "try$this$string" .split(re"\$") .join(",") == "try,this,string"
doAssert "try$this$string" .split(re"\$", 2) .join(",") == "try,this$string"
doAssert "comma, separated, values" .split(re", ") .join("|") == "comma|separated|values"
doAssert "comma, separated, values" .split(re", ", 2) .join("|") == "comma|separated, values"
doAssert "Perl6::Camelia::Test" .split(re"::") .join(",") == "Perl6,Camelia,Test"
doAssert "Perl6::Camelia::Test" .split(re"::", 2) .join(",") == "Perl6,Camelia::Test"
doAssert "split,me,please" .split(re",") .join("|") == "split|me|please"
doAssert "split,me,please" .split(re",", 2) .join("|") == "split|me,please"
doAssert "Hello World Goodbye Mars".split(re"\s+") .join(",") == "Hello,World,Goodbye,Mars"
doAssert "Hello World Goodbye Mars".split(re"\s+", 3).join(",") == "Hello,World,Goodbye Mars"
doAssert "Hello test" .split(re"(\s+)") .join(",") == "Hello, ,test"
doAssert "this will be split" .split(re" ") .join(",") == "this,will,be,split"
doAssert "this will be split" .split(re" ", 3) .join(",") == "this,will,be split"
doAssert "a.b" .split(re"\.") .join(",") == "a,b"
doAssert "" .split(re"") .len == 0
doAssert ":" .split(re"") .len == 1
block: # start position
doAssert "abc".split(re"", start = 1) == @["b", "c"]
doAssert "abc".split(re"", start = 2) == @["c"]
doAssert "abc".split(re"", start = 3) == newSeq[string]()
doAssert "abc".split(re"^b", start = 1) == @["bc"]
block: # replace
block: # replace with 0-length strings
doAssert "".replace(re"1", proc (v: RegexMatch): string = "1") == ""
doAssert " ".replace(re"", proc (v: RegexMatch): string = "1") == "1 1"
doAssert "".replace(re"", proc (v: RegexMatch): string = "1") == "1"
block: # regular replace
doAssert "123".replace(re"\d", "foo") == "foofoofoo"
doAssert "123".replace(re"(\d)", "$1$1") == "112233"
doAssert "123".replace(re"(\d)(\d)", "$1$2") == "123"
doAssert "123".replace(re"(\d)(\d)", "$#$#") == "123"
doAssert "abcdefghijklm".replace(re"(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)", "$12") == "l"
block: # replacing missing captures should throw instead of segfaulting
doAssertRaises(ValueError): discard "ab".replace(re"(a)", "$1$2")
block: # escape strings
block: # escape strings
doAssert "123".escapeRe() == "123"
doAssert "[]".escapeRe() == r"\[\]"
doAssert "()".escapeRe() == r"\(\)"

View File

@@ -1,3 +0,0 @@
# std/nre2 requires nim-regex and it requires nim-unicodedb
exec("nimble --nimbleDir:build/deps install unicodedb@#head")
exec("nimble --nimbleDir:build/deps install regex@#head")

View File

@@ -350,12 +350,3 @@ else:
discard""" discard"""
a() a()
# bug: form feed character in comment should not hang renderTree
macro formfeedComment(): untyped =
result = newNimNode(nnkStmtList)
var c = newNimNode(nnkCommentStmt)
c.strVal = "hello\x0Cworld"
result.add c
formfeedComment()

View File

@@ -1,24 +1,20 @@
discard """ discard """
matrix: "--backend:c --mm:refc; --backend:c --mm:orc; --backend:c --mm:orc --strings:sso; --backend:cpp --mm:refc; --backend:cpp --mm:orc; --backend:js --mm:refc; --backend:js --mm:orc" matrix: "--mm:refc; --mm:orc"
targets: "c cpp js"
""" """
from std/sequtils import toSeq, map from std/sequtils import toSeq, map
from std/sugar import `=>` from std/sugar import `=>`
import std/assertions import std/assertions
const hasNativeSso = defined(nimsso) and
(defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) or defined(gcYrc))
proc tester[T](x: T) = proc tester[T](x: T) =
let test = toSeq(0..4).map(i => newSeq[int]()) let test = toSeq(0..4).map(i => newSeq[int]())
doAssert $test == "@[@[], @[], @[], @[], @[]]" doAssert $test == "@[@[], @[], @[], @[], @[]]"
when not hasNativeSso: func reverse*(a: string): string =
func reverse*(a: string): string = result = a
result = a for i in 0 ..< a.len div 2:
for i in 0 ..< a.len div 2: swap(result[i], result[^(i + 1)])
let j = result.len - i - 1
swap(result[i], result[j])
proc main() = proc main() =
block: # .. block: # ..
@@ -98,164 +94,31 @@ proc main() =
block: # bug #7816 block: # bug #7816
tester(1) tester(1)
when not hasNativeSso: block: # bug #14497, reverse
block: # bug #14497, reverse doAssert reverse("hello") == "olleh"
doAssert reverse("hello") == "olleh"
block: # len, high block: # len, high
var a = "ab\0cd" var a = "ab\0cd"
var b = a.cstring
doAssert a.len == 5 doAssert a.len == 5
doAssert a.high == a.len - 1 block: # bug #16405
when defined(js):
when nimvm: doAssert b.len == 2
else: doAssert b.len == 5
else: doAssert b.len == 2
when not (hasNativeSso and defined(cpp)): doAssert a.high == a.len - 1
let b = a.cstring doAssert b.high == b.len - 1
block: # bug #16405
when defined(js):
when nimvm: doAssert b.len == 2
else: doAssert b.len == 5
else: doAssert b.len == 2
doAssert b.high == b.len - 1
doAssert "".len == 0 doAssert "".len == 0
doAssert "".high == -1 doAssert "".high == -1
when not (hasNativeSso and defined(cpp)): doAssert "".cstring.len == 0
doAssert "".cstring.len == 0 doAssert "".cstring.high == -1
doAssert "".cstring.high == -1
block: # bug #16674 block: # bug #16674
var c: cstring = nil var c: cstring = nil
doAssert c.len == 0 doAssert c.len == 0
doAssert c.high == -1 doAssert c.high == -1
block: # setLen, setLenUninit
when hasNativeSso:
const
alwaysAvail = sizeof(uint) - 1
payloadSize = sizeof(uint) + sizeof(pointer) - 2
longStringDataOffset = 3 * sizeof(int)
template rawSlenOf(s: string): int =
int(cast[ptr byte](unsafeAddr s)[])
template inlineDataOf(s: string): ptr UncheckedArray[char] =
cast[ptr UncheckedArray[char]](cast[uint](unsafeAddr s) + 1'u)
template longDataOf(s: string): ptr UncheckedArray[char] =
let ssPtr = cast[ptr tuple[bytes: uint, more: pointer]](unsafeAddr s)
cast[ptr UncheckedArray[char]](
cast[uint](ssPtr.more) + uint(longStringDataOffset))
proc checkStrInternals(s: string; expectedLen: int) =
doAssert s.len == expectedLen, "expected " & $expectedLen & ", got " & $s.len
when nimvm:
discard
else:
when hasNativeSso and not defined(js) and not defined(nimscript):
# SSO
let rawSlen = rawSlenOf(s)
if rawSlen > payloadSize:
doAssert rawSlen == 255
let data = longDataOf(s)
doAssert data[expectedLen] == '\0'
else:
doAssert rawSlen == expectedLen
let data = inlineDataOf(s)
doAssert data[expectedLen] == '\0'
if expectedLen < alwaysAvail:
for i in expectedLen + 1 ..< alwaysAvail:
doAssert data[i] == '\0'
elif defined(UncheckedArray): # skip JS
# string V2
let cs = s.cstring
let arr = cast[ptr UncheckedArray[char]](unsafeAddr cs[0])
doAssert arr[expectedLen] == '\0'
proc makeStr(n: int): string =
result = newStringOfCap(n)
for i in 0..<n:
result.add char(ord('a') + i mod 26)
proc checkSetLenUninit(oldLen, newLen: int; cmpAfter = -1) =
## Verifies `setLenUninit`:
## - preserves the existing prefix
## - updates the string length
## - keeps internal null termination valid for both shrink and growth
##
## `cmpAfter` is used for layouts where trailing zeroed padding affects
## string comparison semantics after the resize.
var s = makeStr(oldLen)
let prefixLen = min(oldLen, newLen)
let prefix = makeStr(prefixLen)
s.setLenUninit(newLen)
s.checkStrInternals(newLen)
doAssert s[0..<prefixLen] == prefix
if newLen <= oldLen:
doAssert s == prefix
if cmpAfter >= 0:
doAssert s < makeStr(cmpAfter)
const numbers = "1234567890"
block setLen:
# Trim to zero and grow past the old end. Must keep the prefix and zero the tail.
var s = numbers
s.setLen(0)
s.checkStrInternals(0)
doAssert s == ""
s = numbers
s.setLen(numbers.len + 1)
s.checkStrInternals(numbers.len + 1)
doAssert s[0..numbers.high] == numbers
doAssert s[numbers.len] == '\0'
block setLenUninit:
# Shared baseline for both SSO and V2: noop, shrink, grow.
checkSetLenUninit(10, 10)
checkSetLenUninit(10, 5)
checkSetLenUninit(10, 11)
block growingWithinBiggerCapacity:
# Strings can reserve spare capacity even for short strings.
# Growing within that capacity must still update len and the trailing zero.
var s = newStringOfCap(10)
s.add("abc")
s.setLenUninit(6)
s.checkStrInternals(6)
doAssert s[0..2] == "abc"
when hasNativeSso:
const
shortLen = alwaysAvail
medLen = payloadSize
longLen = payloadSize + 8
# Staying short and verify short-compare padding after shrink.
checkSetLenUninit(shortLen, shortLen - 1, shortLen)
checkSetLenUninit(shortLen - 2, shortLen - 1)
checkSetLenUninit(shortLen, 0)
# Cross the short/medium boundary in both directions.
checkSetLenUninit(medLen, medLen - 1)
checkSetLenUninit(medLen, alwaysAvail - 1, alwaysAvail)
checkSetLenUninit(alwaysAvail, medLen)
# Cross the inline/long boundary in both directions and cover long growth.
checkSetLenUninit(longLen, longLen - 2)
checkSetLenUninit(longLen, medLen - 1)
checkSetLenUninit(longLen, alwaysAvail - 1, alwaysAvail)
checkSetLenUninit(medLen, longLen)
checkSetLenUninit(longLen, longLen + 10)
checkSetLenUninit(longLen, 0)
when not defined(js) and not defined(nimscript):
# shared long strings must not mutate the original when grown
let src = makeStr(longLen)
var orig = src
var copy = orig
copy.setLenUninit(longLen + 4)
copy.checkStrInternals(longLen + 4)
doAssert orig == src
doAssert copy[0..<longLen] == src
static: main() static: main()
main() main()

View File

@@ -1,7 +0,0 @@
discard """
matrix: "--strings:sso --mm:orc"
targets: "c cpp"
"""
var s = "abc"
discard s.cstring

View File

@@ -1,10 +0,0 @@
discard """
timeout: "1.0"
"""
type
Generic[T] = object
t: T
A = Generic[B]
B = Generic[A]

View File

@@ -1,7 +0,0 @@
discard """
errormsg: "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
"""
type
Foo = set[Bar]
Bar = int32

View File

@@ -1,8 +0,0 @@
discard """
errormsg: "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
"""
type
Foo = int32
Bar = set[Baz]
Baz = Foo