mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 19:33:42 +00:00
Compare commits
89 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f500679b1 | ||
|
|
93cb5889f4 | ||
|
|
2b075fc87d | ||
|
|
8f7fd28692 | ||
|
|
74b30de1c6 | ||
|
|
6947d96b5f | ||
|
|
689111936c | ||
|
|
34a594abec | ||
|
|
f10dda264c | ||
|
|
c5455c1515 | ||
|
|
334f2d6a87 | ||
|
|
29125f0bc7 | ||
|
|
becb06dd70 | ||
|
|
2ddeabceaf | ||
|
|
9fa69d222f | ||
|
|
d48fc130c1 | ||
|
|
c86e7a0ca9 | ||
|
|
8d553c5624 | ||
|
|
d9ed8f2717 | ||
|
|
35429f6252 | ||
|
|
7277d95b3c | ||
|
|
ba8ff51f2d | ||
|
|
a4982bd74d | ||
|
|
196f444699 | ||
|
|
67446d192d | ||
|
|
97c160271c | ||
|
|
c022120ebb | ||
|
|
e45bef46a1 | ||
|
|
a21a1c99cd | ||
|
|
f3f76cdc52 | ||
|
|
e6413f8fe4 | ||
|
|
fa6be0fb6a | ||
|
|
f04fe4e4a3 | ||
|
|
d1016a3bc9 | ||
|
|
0ae9f5e4df | ||
|
|
fda7c2e5bb | ||
|
|
237f50e0d9 | ||
|
|
34e61d5ae6 | ||
|
|
1dafcbd2a7 | ||
|
|
57e4b364d2 | ||
|
|
6d32ceaebb | ||
|
|
72e24284ef | ||
|
|
f4fd26d403 | ||
|
|
a4ec5d6be2 | ||
|
|
75f818f385 | ||
|
|
919d0d5ea8 | ||
|
|
34c9606b41 | ||
|
|
a69ab81fa9 | ||
|
|
90b7517656 | ||
|
|
244af30c58 | ||
|
|
795b5665dc | ||
|
|
c8556ef5df | ||
|
|
6c8ab9f898 | ||
|
|
547416b806 | ||
|
|
97c41c0f15 | ||
|
|
feaa364038 | ||
|
|
05c8e1d85d | ||
|
|
90efe870c8 | ||
|
|
860e827df6 | ||
|
|
834c35a137 | ||
|
|
75f01bd49f | ||
|
|
800384176e | ||
|
|
dc8f1ab52d | ||
|
|
ffaabdf21e | ||
|
|
431e01eaf2 | ||
|
|
a5e73ff408 | ||
|
|
37fd04a0eb | ||
|
|
dc1c3ed90e | ||
|
|
8d475993f8 | ||
|
|
8914baae78 | ||
|
|
fcf4f10c70 | ||
|
|
5394c6814b | ||
|
|
2ddda806a9 | ||
|
|
8775ef62fe | ||
|
|
63398611e7 | ||
|
|
a6988458c4 | ||
|
|
9346b138e1 | ||
|
|
c75c85cbf8 | ||
|
|
38384d040a | ||
|
|
605180fcfa | ||
|
|
be6e195585 | ||
|
|
873ab1f9ef | ||
|
|
4efece995e | ||
|
|
40e1a34831 | ||
|
|
bd9cbc91ba | ||
|
|
d7ae00349a | ||
|
|
61e98e9bf1 | ||
|
|
7f6c0afa59 | ||
|
|
6b89497b1e |
2
.github/workflows/ci_docs.yml
vendored
2
.github/workflows/ci_docs.yml
vendored
@@ -45,7 +45,7 @@ jobs:
|
||||
- target: windows
|
||||
os: windows-latest
|
||||
- target: osx
|
||||
os: macos-13
|
||||
os: macos-15
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -68,6 +68,7 @@ testament.db
|
||||
/csources
|
||||
/csources_v1
|
||||
/csources_v2
|
||||
/csources_v3
|
||||
|
||||
/dist/
|
||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
||||
|
||||
@@ -28,12 +28,12 @@ jobs:
|
||||
# # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
||||
# vmImage: 'ubuntu-18.04'
|
||||
# CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-13'
|
||||
CPU: amd64
|
||||
OSX_arm64:
|
||||
vmImage: 'macos-15'
|
||||
CPU: arm64
|
||||
OSX_arm64_cpp:
|
||||
vmImage: 'macos-15'
|
||||
CPU: arm64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
vmImage: 'windows-2025'
|
||||
|
||||
22
changelog.md
22
changelog.md
@@ -27,6 +27,14 @@ errors.
|
||||
|
||||
- With `-d:nimPreviewDuplicateModuleError`, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Use `import foo as foo1` (or other aliases) to avoid collisions.
|
||||
|
||||
- Adds the switch `--mangle:nim|cpp`, which selects `nim` or `cpp` style name mangling when used with `debuginfo` on, defaults to `nim`. The default is changed from `cpp` to `nim`.
|
||||
|
||||
- The second parameter of `succ`, `pred`, `inc`, and `dec` in `system` now accepts `SomeInteger` (previously `Ordinal`).
|
||||
|
||||
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
|
||||
|
||||
- Adds a new warning enabled by `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts are not warned on.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Additions:"
|
||||
@@ -53,6 +61,10 @@ errors.
|
||||
|
||||
- `system.setLenUninit` now supports refc, JS and VM backends.
|
||||
|
||||
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
|
||||
Modes include `Nim` (default, fully compatible) and two new experimental modes:
|
||||
`Lax` and `Gnu` for different option parsing behaviors.
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
- `std/math` The `^` symbol now supports floating-point as exponent in addition to the Natural type.
|
||||
@@ -99,7 +111,17 @@ errors.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- Fixed a bug where `sizeof(T)` inside a `typedesc` template called from a generic type's
|
||||
`when` clause would error with "'sizeof' requires '.importc' types to be '.completeStruct'".
|
||||
The issue was that `hasValuelessStatics` in `semtypinst.nim` didn't recognize
|
||||
`tyTypeDesc(tyGenericParam)` as an unresolved generic parameter.
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Added `--raw` flag when generating JSON docs to not render markup.
|
||||
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
|
||||
- Added `--styleCheck:warning` flag to treat style check violations as warnings.
|
||||
|
||||
## Documentation changes
|
||||
|
||||
- Added documentation for the `completeStruct` pragma in the manual.
|
||||
|
||||
@@ -500,6 +500,7 @@ type
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mExit,
|
||||
mSetLengthStr, mSetLengthSeq,
|
||||
mSetLengthSeqUninit,
|
||||
mIsPartOf, mAstToStr, mParallel,
|
||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
|
||||
1160
compiler/astdef.nim
Normal file
1160
compiler/astdef.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -660,9 +660,9 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mSubF64: applyFormat("(($4)($1) - ($4)($2))")
|
||||
of mMulF64: applyFormat("(($4)($1) * ($4)($2))")
|
||||
of mDivF64: applyFormat("(($4)($1) / ($4)($2))")
|
||||
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))")
|
||||
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))")
|
||||
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))")
|
||||
of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2 & ($5 - 1)))")
|
||||
of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2 & ($5 - 1)))")
|
||||
of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2 & ($5 - 1)))")
|
||||
of mBitandI: applyFormat("($4)($1 & $2)")
|
||||
of mBitorI: applyFormat("($4)($1 | $2)")
|
||||
of mBitxorI: applyFormat("($4)($1 ^ $2)")
|
||||
@@ -766,6 +766,10 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
||||
return
|
||||
else:
|
||||
a = initLocExprSingleUse(p, e[0])
|
||||
|
||||
if e.typ != nil and e.typ.kind == tyObject:
|
||||
# bug #23453 #25265
|
||||
discard getTypeDesc(p.module, e.typ)
|
||||
if d.k == locNone:
|
||||
# dest = *a; <-- We do not know that 'dest' is on the heap!
|
||||
# It is completely wrong to set 'd.storage' here, unless it's not yet
|
||||
@@ -848,7 +852,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
|
||||
var
|
||||
i: int = 0
|
||||
var a: TLoc = initLocExpr(p, e[0])
|
||||
let tupType = a.t.skipTypes(abstractInst+{tyVar})
|
||||
let tupType = a.t.skipTypes(abstractInst+{tyVar}+tyUserTypeClasses) # ref #25227
|
||||
assert tupType.kind == tyTuple
|
||||
d.inheritLocation(a)
|
||||
discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
|
||||
@@ -1921,7 +1925,14 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
|
||||
else: internalError(p.config, e.info, "genArrayLen()")
|
||||
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
proc isTrivialTypesToSnippet(t: PType): Rope =
|
||||
if containsGarbageCollectedRef(t) or
|
||||
hasDestructor(t):
|
||||
result = rope"NIM_FALSE"
|
||||
else:
|
||||
result = rope"NIM_TRUE"
|
||||
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc, noinit = false) =
|
||||
if optSeqDestructors in p.config.globalOptions:
|
||||
e[1] = makeAddr(e[1], p.module.idgen)
|
||||
genCall(p, e, d)
|
||||
@@ -1934,17 +1945,22 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
let t = skipTypes(e[1].typ, {tyVar})
|
||||
|
||||
var call = initLoc(locCall, e, OnHeap)
|
||||
let name = if noinit: "setLengthSeqUninit" else: "setLengthSeqV2"
|
||||
if not p.module.compileToCpp:
|
||||
const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
|
||||
const setLenPattern = "($3) #$6(($1)?&($1)->Sup:NIM_NIL, $4, $2, $5)"
|
||||
call.snippet = ropecg(p.module, setLenPattern, [
|
||||
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info),
|
||||
isTrivialTypesToSnippet(t.skipTypes(abstractInst)[0]),
|
||||
name])
|
||||
|
||||
else:
|
||||
const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
|
||||
const setLenPattern = "($3) #$6($1, $4, $2, $5)"
|
||||
call.snippet = ropecg(p.module, setLenPattern, [
|
||||
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
|
||||
genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info),
|
||||
isTrivialTypesToSnippet(t.skipTypes(abstractInst)[0]),
|
||||
name])
|
||||
|
||||
genAssignment(p, a, call, {})
|
||||
gcUsage(p.config, e)
|
||||
@@ -2567,6 +2583,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
|
||||
of mSetLengthStr: genSetLengthStr(p, e, d)
|
||||
of mSetLengthSeq: genSetLengthSeq(p, e, d)
|
||||
of mSetLengthSeqUninit: genSetLengthSeq(p, e, d, noinit = true)
|
||||
of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mInSet, mXorSet:
|
||||
genSetOp(p, e, d, op)
|
||||
@@ -2836,7 +2853,11 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
raiseInstr(p, p.s(cpsStmts))
|
||||
linefmt p, cpsStmts, "}$n", []
|
||||
|
||||
if n[0].typ.kind != tyObject:
|
||||
# skip cast when types map to the same C type
|
||||
# this avoids invalid C code like `*(T*)&x` for types that can't have their address taken (e.g., WASM __externref_t)
|
||||
if getTypeDesc(p.module, n.typ) == getTypeDesc(p.module, n[0].typ):
|
||||
expr(p, n[0], d)
|
||||
elif n[0].typ.kind != tyObject:
|
||||
if n.isLValue:
|
||||
putIntoDest(p, d, n,
|
||||
"(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
@@ -2864,7 +2885,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
|
||||
var a: TLoc = initLocExpr(p, arg)
|
||||
putIntoDest(p, d, n,
|
||||
"(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
|
||||
elif p.module.compileToCpp:
|
||||
elif p.module.compileToCpp or isImportedType(src):
|
||||
# C++ implicitly downcasts for us
|
||||
expr(p, arg, d)
|
||||
else:
|
||||
|
||||
@@ -202,7 +202,7 @@ proc genState(p: BProc, n: PNode) =
|
||||
elif n0.kind == nkStrLit:
|
||||
linefmt(p, cpsStmts, "$1: ;$n", [n0.strVal])
|
||||
|
||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt = false) =
|
||||
# Called by return and break stmts.
|
||||
# Deals with issues faced when jumping out of try/except/finally stmts.
|
||||
|
||||
@@ -234,7 +234,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||
|
||||
# Pop exceptions that was handled by the
|
||||
# except-blocks we are in
|
||||
if noSafePoints notin p.flags:
|
||||
if noSafePoints notin p.flags and not (isReturnStmt and isClosureIterator(p.prc.typ)):
|
||||
for i in countdown(howManyExcepts-1, 0):
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
|
||||
@@ -509,7 +509,8 @@ proc genReturnStmt(p: BProc, t: PNode) =
|
||||
if (t[0].kind != nkEmpty): genStmts(p, t[0])
|
||||
blockLeaveActions(p,
|
||||
howManyTrys = p.nestedTryStmts.len,
|
||||
howManyExcepts = p.inExceptBlockLen)
|
||||
howManyExcepts = p.inExceptBlockLen,
|
||||
isReturnStmt = true)
|
||||
if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags:
|
||||
# If we're in a finally block, and we came here by exception
|
||||
# consume it before we return.
|
||||
|
||||
@@ -75,7 +75,7 @@ proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
|
||||
proc fillBackendName(m: BModule; s: PSym) =
|
||||
if s.loc.snippet == "":
|
||||
var result: Rope
|
||||
if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
|
||||
if s.kind in routineKinds and {optCDebug, optItaniumMangle} * m.g.config.globalOptions == {optCDebug, optItaniumMangle} and
|
||||
m.g.config.symbolFiles == disabledSf:
|
||||
result = mangleProc(m, s, false).rope
|
||||
else:
|
||||
@@ -247,6 +247,7 @@ proc isOrHasImportedCppType(typ: PType): bool =
|
||||
searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)
|
||||
|
||||
proc hasNoInit(t: PType): bool =
|
||||
let t = skipTypes(t, {tyGenericInst})
|
||||
result = t.sym != nil and sfNoInit in t.sym.flags
|
||||
|
||||
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope
|
||||
@@ -298,7 +299,12 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
|
||||
result = tab.getOrDefault(sig)
|
||||
|
||||
proc addAbiCheck(m: BModule; t: PType, name: Rope) =
|
||||
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize):
|
||||
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize) and
|
||||
not (t.kind == tyObject and searchTypeFor(t, proc (t: PType): bool {.nimcall.} = t.kind == tyUncheckedArray)):
|
||||
# `UncheckedArray`, not `ptr UncheckedArray` type field in object types is a flexible array.
|
||||
# `sizeof` in C and Nim doesn't always return the same value for object types containing it.
|
||||
# making `getSize` in Nim always returns the same value as `sizeof` in C from flexible arrays seems hard.
|
||||
# See `SEQ_DECL_SIZE` in lib/nimbase.h
|
||||
var msg = "backend & Nim disagree on size for: "
|
||||
msg.addTypeHeader(m.config, t)
|
||||
var msg2 = ""
|
||||
@@ -1064,6 +1070,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
else: getTupleDesc(m, t, result, check)
|
||||
if not isImportedType(t):
|
||||
m.s[cfsTypes].add(recdesc)
|
||||
addAbiCheck(m, t, result)
|
||||
elif tfIncompleteStruct notin t.flags:
|
||||
discard # addAbiCheck(m, t, result) # already handled elsewhere
|
||||
of tySet:
|
||||
|
||||
@@ -315,7 +315,10 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||
let line = t.info.safeLineNm
|
||||
|
||||
if optEmbedOrigSrc in p.config.globalOptions:
|
||||
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
|
||||
var code = sourceLine(p.config, t.info)
|
||||
if code.endsWith('\\'):
|
||||
code.add "#"
|
||||
p.s(cpsStmts).add("// " & code & "\L")
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
let freshLine = freshLineInfo(p, t.info)
|
||||
if freshLine:
|
||||
@@ -2183,7 +2186,10 @@ proc handleProcGlobals(m: BModule) =
|
||||
|
||||
# fixes recursive calls #24997
|
||||
swap stmts, m.preInitProc.s(cpsStmts)
|
||||
genStmts(m.preInitProc, procGlobals[i])
|
||||
var transformedN = procGlobals[i]
|
||||
if sfInjectDestructors in m.module.flags:
|
||||
transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
|
||||
genStmts(m.preInitProc, transformedN)
|
||||
swap stmts, m.preInitProc.s(cpsStmts)
|
||||
|
||||
handleProcGlobals(m)
|
||||
|
||||
@@ -139,8 +139,7 @@
|
||||
|
||||
import
|
||||
ast, msgs, idents,
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
|
||||
options
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
|
||||
|
||||
import std/tables
|
||||
|
||||
@@ -174,12 +173,14 @@ type
|
||||
tempVarId: int # unique name counter
|
||||
hasExceptions: bool # Does closure have yield in try?
|
||||
curExcLandingState: PNode
|
||||
curExceptLevel: int
|
||||
curFinallyLevel: int
|
||||
idgen: IdGenerator
|
||||
varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
|
||||
finallyPathLen: PNode # int literal
|
||||
|
||||
nullifyCurExc: PNode # Empty node, if no yields in tries
|
||||
restoreExternExc: PNode # Empty node, id no yields in tries
|
||||
|
||||
const
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt, nkTypeOfExpr} + procDefs
|
||||
@@ -311,7 +312,7 @@ proc hasYields(n: PNode): bool =
|
||||
break
|
||||
|
||||
proc newNullifyCurExc(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
# :curEcx = nil
|
||||
# :curExc = nil
|
||||
let curExc = ctx.newCurExcAccess()
|
||||
curExc.info = info
|
||||
let nilnode = newNodeIT(nkNilLit, info, getSysType(ctx.g, info, tyNil))
|
||||
@@ -862,7 +863,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
retStmt.flags.incl(nfNoRewrite)
|
||||
|
||||
let ifBody = newTree(nkIfStmt,
|
||||
newTree(nkElifBranch, excNilCmp, retStmt),
|
||||
newTree(nkElifBranch, excNilCmp, newTree(nkStmtList, ctx.newRestoreExternException(), retStmt)),
|
||||
newTree(nkElse,
|
||||
newTree(nkStmtList,
|
||||
newTreeI(nkRaiseStmt, info, ctx.g.emptyNode))))
|
||||
@@ -917,16 +918,15 @@ proc transformBreakStmt(ctx: var Ctx, n: PNode): PNode =
|
||||
result = n
|
||||
|
||||
proc transformReturnStmt(ctx: var Ctx, n: PNode): PNode =
|
||||
# "Returning" involves jumping along all the cureent finally path.
|
||||
# "Returning" involves jumping along all the current finally path.
|
||||
# The last finally should exit to state 0 which is a special case for last exit
|
||||
# (either return or propagating exception to the caller).
|
||||
# It is eccounted for in newEndFinallyNode.
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
# Returns prevent exception propagation
|
||||
result.add(ctx.newNullifyCurExc(n.info))
|
||||
result.add(ctx.nullifyCurExc)
|
||||
|
||||
result.add(ctx.newRestoreExternException())
|
||||
|
||||
var finallyChain = newSeq[PNode]()
|
||||
|
||||
@@ -950,6 +950,7 @@ proc transformReturnStmt(ctx: var Ctx, n: PNode): PNode =
|
||||
result.add(ctx.newJumpAlongFinallyChain(finallyChain, n.info))
|
||||
else:
|
||||
# There are no (split) finallies on the path, so we can return right away
|
||||
result.add(ctx.restoreExternExc)
|
||||
result.add(n)
|
||||
|
||||
proc transformBreaksAndReturns(ctx: var Ctx, n: PNode): PNode =
|
||||
@@ -960,7 +961,7 @@ proc transformBreaksAndReturns(ctx: var Ctx, n: PNode): PNode =
|
||||
# of nkContinueStmt: # By this point all relevant continues should be
|
||||
# lowered to breaks in transf.nim.
|
||||
of nkReturnStmt:
|
||||
if ctx.curFinallyLevel > 0 and nfNoRewrite notin n.flags:
|
||||
if nfNoRewrite notin n.flags:
|
||||
result = ctx.transformReturnStmt(n)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
@@ -994,8 +995,7 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
|
||||
of nkYieldStmt:
|
||||
result = addGotoOut(result, gotoOut)
|
||||
if ctx.curExceptLevel > 0 or ctx.curFinallyLevel > 0:
|
||||
result = newTree(nkStmtList, ctx.newRestoreExternException(), result)
|
||||
result = newTree(nkStmtList, ctx.restoreExternExc, result)
|
||||
|
||||
of nkElse, nkElseExpr:
|
||||
result[0] = addGotoOut(result[0], gotoOut)
|
||||
@@ -1107,7 +1107,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
tryBody = ctx.transformClosureIteratorBody(tryBody, tryOut)
|
||||
|
||||
if exceptBody.kind != nkEmpty:
|
||||
inc ctx.curExceptLevel
|
||||
ctx.curExcLandingState = if finallyBody.kind != nkEmpty: finallyLabel
|
||||
else: oldExcLandingState
|
||||
discard ctx.newState(exceptBody, false, exceptLabel)
|
||||
@@ -1116,7 +1115,6 @@ proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode
|
||||
exceptBody = ctx.addElseToExcept(exceptBody, normalOut)
|
||||
# echo "EXCEPT: ", renderTree(exceptBody)
|
||||
exceptBody = ctx.transformClosureIteratorBody(exceptBody, tryOut)
|
||||
inc ctx.curExceptLevel
|
||||
|
||||
ctx.curExcLandingState = oldExcLandingState
|
||||
|
||||
@@ -1308,16 +1306,15 @@ proc countStateOccurences(ctx: var Ctx, n: PNode, stateOccurences: var openArray
|
||||
|
||||
proc replaceDeletedStates(ctx: var Ctx, n: PNode): PNode =
|
||||
result = n
|
||||
for i in 0 ..< n.safeLen:
|
||||
let c = n[i]
|
||||
if c.kind == nkIntLit:
|
||||
let idx = c.intVal
|
||||
if idx >= 0 and idx < ctx.states.len and ctx.states[idx].label == c and ctx.states[idx].deletable:
|
||||
let gt = ctx.replaceDeletedStates(skipStmtList(ctx.states[idx].body))
|
||||
assert(gt.kind == nkGotoState)
|
||||
n[i] = gt[0]
|
||||
else:
|
||||
n[i] = ctx.replaceDeletedStates(c)
|
||||
if n.kind == nkIntLit:
|
||||
let idx = n.intVal
|
||||
if idx >= 0 and idx < ctx.states.len and ctx.states[idx].label == n and ctx.states[idx].deletable:
|
||||
let gt = ctx.replaceDeletedStates(skipStmtList(ctx.states[idx].body))
|
||||
assert(gt.kind == nkGotoState)
|
||||
result = gt[0]
|
||||
else:
|
||||
for i in 0 ..< n.safeLen:
|
||||
n[i] = ctx.replaceDeletedStates(n[i])
|
||||
|
||||
proc replaceInlinedStates(ctx: var Ctx, n: PNode): PNode =
|
||||
## Find all nkGotoState(stateIdx) nodes that do not follow nkYield.
|
||||
@@ -1348,6 +1345,7 @@ proc optimizeStates(ctx: var Ctx) =
|
||||
# Replace deletable state labels to labels of respective non-empty states
|
||||
for i in 0 .. ctx.states.high:
|
||||
ctx.states[i].body = ctx.replaceDeletedStates(ctx.states[i].body)
|
||||
ctx.states[i].excLandingState = ctx.replaceDeletedStates(ctx.states[i].excLandingState)
|
||||
|
||||
# Remove deletable states
|
||||
var i = 0
|
||||
@@ -1469,13 +1467,17 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
|
||||
ctx.curExcLandingState = ctx.newStateLabel()
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
|
||||
|
||||
|
||||
ctx.nullifyCurExc = newTree(nkStmtList)
|
||||
ctx.restoreExternExc = newTree(nkStmtList)
|
||||
|
||||
var n = n.toStmtList
|
||||
# echo "transformed into ", n
|
||||
|
||||
discard ctx.newState(n, false, nil)
|
||||
|
||||
let finalState = ctx.newStateLabel()
|
||||
let gotoOut = newTree(nkGotoState, finalState)
|
||||
let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
|
||||
|
||||
var ns = false
|
||||
n = ctx.lowerStmtListExprs(n, ns)
|
||||
@@ -1487,11 +1489,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
# Splitting transformation
|
||||
discard ctx.transformClosureIteratorBody(n, gotoOut)
|
||||
|
||||
let finalStateBody = newTree(nkStmtList)
|
||||
if ctx.hasExceptions:
|
||||
finalStateBody.add(ctx.newRestoreExternException())
|
||||
finalStateBody.add(newTree(nkGotoState, g.newIntLit(n.info, -1)))
|
||||
discard ctx.newState(finalStateBody, true, finalState)
|
||||
ctx.nullifyCurExc.add(ctx.newNullifyCurExc(fn.info))
|
||||
ctx.restoreExternExc.add(ctx.newRestoreExternException())
|
||||
|
||||
# Assign state label indexes
|
||||
for i in 0 .. ctx.states.high:
|
||||
@@ -1510,7 +1510,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
let body = ctx.transformStateAssignments(s.body)
|
||||
caseDispatcher.add newTreeI(nkOfBranch, body.info, s.label, body)
|
||||
|
||||
caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
|
||||
caseDispatcher.add newTreeI(nkElse, n.info,
|
||||
newTree(nkStmtList, ctx.restoreExternExc,
|
||||
newTreeI(nkReturnStmt, n.info, g.emptyNode)))
|
||||
|
||||
result = wrapIntoStateLoop(ctx, caseDispatcher)
|
||||
result = liftLocals(ctx, result)
|
||||
|
||||
@@ -118,7 +118,7 @@ const
|
||||
errInvalidCmdLineOption = "invalid command line option: '$1'"
|
||||
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
|
||||
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint', 'warning', 'error' or 'usages' expected, but '$1' found"
|
||||
|
||||
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
|
||||
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
|
||||
@@ -364,6 +364,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
result = false
|
||||
of "panics": result = contains(conf.globalOptions, optPanics)
|
||||
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
|
||||
of "mangle": result = contains(conf.globalOptions, optItaniumMangle)
|
||||
else:
|
||||
result = false
|
||||
invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
@@ -760,6 +761,14 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf.globalOptions.excl optCDebug
|
||||
else:
|
||||
localError(conf, info, "expected native|gdb|on|off but found " & arg)
|
||||
of "mangle":
|
||||
case arg.normalize
|
||||
of "nim":
|
||||
conf.globalOptions.excl optItaniumMangle
|
||||
of "cpp":
|
||||
conf.globalOptions.incl optItaniumMangle
|
||||
else:
|
||||
localError(conf, info, "expected nim|cpp but found " & arg)
|
||||
of "g": # alias for --debugger:native
|
||||
conf.globalOptions.incl optCDebug
|
||||
conf.options.incl optLineDir
|
||||
@@ -882,11 +891,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "import":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.implicitImports.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitImports.add m
|
||||
of "include":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.implicitIncludes.add findModule(conf, arg, toFullPath(conf, info)).string
|
||||
let m = findModule(conf, arg, toFullPath(conf, info)).string
|
||||
if m.len == 0:
|
||||
localError(conf, info, "Cannot resolve filename: " & arg)
|
||||
else:
|
||||
conf.implicitIncludes.add m
|
||||
of "listcmd":
|
||||
processOnOffSwitchG(conf, {optListCmd}, arg, pass, info)
|
||||
of "asm":
|
||||
@@ -1080,6 +1097,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "shownonexports":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
showNonExportedFields(conf)
|
||||
of "raw":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
docRawOutput(conf)
|
||||
of "exceptions":
|
||||
case arg.normalize
|
||||
of "cpp": conf.exc = excCpp
|
||||
@@ -1111,9 +1131,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "stylecheck":
|
||||
case arg.normalize
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
|
||||
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError, optStyleWarning}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError, optStyleWarning}
|
||||
of "warning": conf.globalOptions = conf.globalOptions + {optStyleWarning} - {optStyleHint, optStyleError}
|
||||
of "error": conf.globalOptions = conf.globalOptions + {optStyleError} - {optStyleHint, optStyleWarning}
|
||||
of "usages": conf.globalOptions.incl optStyleUsages
|
||||
else: localError(conf, info, errOffHintsError % arg)
|
||||
of "showallmismatches":
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
|
||||
import std/intsets
|
||||
import std/sets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -73,18 +73,20 @@ type
|
||||
MatchFlags* = enum
|
||||
mfDontBind # Do not bind generic parameters
|
||||
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
|
||||
|
||||
|
||||
ConceptTypePair = tuple[conceptId, typeId: ItemId]
|
||||
## Pair of (concept type id, implementation type id) used for cycle detection
|
||||
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
bindings: LayeredIdTable
|
||||
marker: IntSet ## Some protection against wild runaway recursions.
|
||||
marker: HashSet[ConceptTypePair] ## Tracks (concept, type) pairs being checked to detect cycles.
|
||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType ## current concept being evaluated
|
||||
depthCount = 0
|
||||
flags: set[MatchFlags]
|
||||
|
||||
|
||||
MatchKind = enum
|
||||
mkNoMatch, mkSubset, mkSame
|
||||
|
||||
@@ -137,7 +139,7 @@ proc bindParam(c: PContext, m: var MatchCon; key, v: PType): bool {. discardable
|
||||
# check previously bound value
|
||||
if not matchType(c, old, value, m):
|
||||
return false
|
||||
elif key.hasElementType and key.elementType.kind != tyNone:
|
||||
elif key.hasElementType and not key.elementType.isNil and key.elementType.kind != tyNone:
|
||||
# check constaint
|
||||
if matchType(c, unrollGenericParam(key), value, m) == false:
|
||||
return false
|
||||
@@ -188,32 +190,48 @@ iterator traverseTyOr(t: PType): PType {. closure .}=
|
||||
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
|
||||
assert not(potentialImpl.reduceToBase.kind == tyConcept)
|
||||
let concpt = f.reduceToBase
|
||||
if m.depthCount > 0:
|
||||
# concepts that are more then 2 levels deep are treated like
|
||||
# tyAnything to stop dependencies from getting out of control
|
||||
|
||||
# Handle self-referential concepts: when a concept references itself in its body
|
||||
# (e.g., `A = concept; proc test(x: Self, y: A)`), the inner type A has n=nil.
|
||||
# We detect this by checking if the concept has the same symbol name as the
|
||||
# one we're currently matching and has no body (n=nil).
|
||||
if concpt.n.isNil:
|
||||
if concpt.sym != nil and m.concpt.sym != nil and
|
||||
concpt.sym == m.concpt.sym:
|
||||
# Self-reference: check if potentialImpl matches what we're already checking
|
||||
return potentialImpl.id == m.potentialImplementation.id
|
||||
# Concept without body that's not a self-reference - cannot match
|
||||
return false
|
||||
|
||||
# Cycle detection: track (concept, type) pairs to prevent infinite recursion.
|
||||
# Returns true on cycle (coinductive semantics) to support co-dependent concepts.
|
||||
let pair: ConceptTypePair = (concpt.itemId, potentialImpl.itemId)
|
||||
if pair in m.marker:
|
||||
return true
|
||||
m.marker.incl pair
|
||||
|
||||
var efPot = potentialImpl
|
||||
if potentialImpl.isSelf:
|
||||
if m.concpt.n == concpt.n:
|
||||
m.marker.excl pair
|
||||
return true
|
||||
efPot = m.potentialImplementation
|
||||
|
||||
|
||||
var oldBindings = m.bindings
|
||||
m.bindings = newTypeMapLayer(m.bindings)
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = efPot
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = concpt
|
||||
|
||||
|
||||
var invocation: PType = nil
|
||||
if f.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
invocation = f
|
||||
inc m.depthCount
|
||||
result = processConcept(c, concpt, invocation, oldBindings, m)
|
||||
dec m.depthCount
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.bindings = oldBindings
|
||||
m.marker.excl pair
|
||||
|
||||
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
|
||||
if fn.kind != an.kind:
|
||||
@@ -263,6 +281,22 @@ proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
|
||||
return mkNoMatch
|
||||
return mkSubset
|
||||
|
||||
proc isObjectSubtype(f, a: PType): bool =
|
||||
var t = a
|
||||
result = false
|
||||
while t != nil:
|
||||
t = t.baseClass
|
||||
if t == nil:
|
||||
break
|
||||
t = t.skipTypes({tyPtr,tyRef})
|
||||
if t == nil:
|
||||
break
|
||||
if t.kind != tyObject:
|
||||
break
|
||||
if sameObjectTypes(f, t):
|
||||
result = true
|
||||
break
|
||||
|
||||
proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
||||
@@ -327,6 +361,8 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
result = a.base.sym == f.sym
|
||||
else:
|
||||
result = sameType(f, a)
|
||||
if not result and f.kind == tyObject and a.kind == tyObject:
|
||||
result = isObjectSubtype(f, a)
|
||||
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
|
||||
result = a.skipTypes(ignorableForArgType).kind == f.kind
|
||||
of tyBool, tyChar, tyInt..tyUInt64:
|
||||
@@ -358,6 +394,14 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
if not matchType(c, f[i], ea[i], m):
|
||||
result = false
|
||||
break
|
||||
elif f.kind == tyGenericInvocation:
|
||||
# bind potential generic constraints into body
|
||||
let body = f.base
|
||||
for i in 1 ..< len(f):
|
||||
bindParam(c,m,body[i-1], f[i])
|
||||
result = matchType(c, body, a, m)
|
||||
else: # tyGenericInst
|
||||
result = matchType(c, f.last, a, m)
|
||||
of tyOrdinal:
|
||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
||||
of tyStatic:
|
||||
@@ -584,7 +628,7 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
|
||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags, marker: initHashSet[ConceptTypePair]())
|
||||
if arg.isConcept:
|
||||
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
|
||||
elif arg.acceptsAllTypes:
|
||||
|
||||
@@ -173,3 +173,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasXorSet")
|
||||
|
||||
defineSymbol("nimHasPreviewDuplicateModuleError")
|
||||
defineSymbol("nimHasSetLengthSeqUninitMagic")
|
||||
|
||||
defineSymbol("nimHasImplicitRangeConversion")
|
||||
|
||||
340
compiler/deps.nim
Normal file
340
compiler/deps.nim
Normal file
@@ -0,0 +1,340 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2025 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Generate a .build.nif file for nifmake from a Nim project.
|
||||
## This enables incremental and parallel compilation using the `m` switch.
|
||||
|
||||
import std / [os, tables, sets, times, osproc, strutils]
|
||||
import options, msgs, lineinfos
|
||||
|
||||
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
|
||||
import "../dist/nimony/src/gear2" / modnames
|
||||
|
||||
type
|
||||
FilePair = object
|
||||
nimFile: string
|
||||
modname: string
|
||||
|
||||
Node = ref object
|
||||
files: seq[FilePair] # main file + includes
|
||||
deps: seq[int] # indices into DepContext.nodes
|
||||
id: int
|
||||
|
||||
DepContext = object
|
||||
config: ConfigRef
|
||||
nifler: string
|
||||
nodes: seq[Node]
|
||||
processedModules: Table[string, int] # modname -> node index
|
||||
includeStack: seq[string]
|
||||
|
||||
proc toPair(c: DepContext; f: string): FilePair =
|
||||
FilePair(nimFile: f, modname: moduleSuffix(f, cast[seq[string]](c.config.searchPaths)))
|
||||
|
||||
proc depsFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".deps.nif"
|
||||
|
||||
proc parsedFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".p.nif"
|
||||
|
||||
proc semmedFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".nif"
|
||||
|
||||
proc findNifler(): string =
|
||||
# Look for nifler in common locations
|
||||
result = findExe("nifler")
|
||||
if result.len == 0:
|
||||
# Try relative to nim executable
|
||||
let nimDir = getAppDir()
|
||||
result = nimDir / "nifler"
|
||||
if not fileExists(result):
|
||||
result = nimDir / ".." / "nimony" / "bin" / "nifler"
|
||||
if not fileExists(result):
|
||||
result = ""
|
||||
|
||||
proc runNifler(c: DepContext; nimFile: string): bool =
|
||||
## Run nifler deps on a file if needed. Returns true on success.
|
||||
let pair = c.toPair(nimFile)
|
||||
let depsPath = c.depsFile(pair)
|
||||
|
||||
# Check if deps file is up-to-date
|
||||
if fileExists(depsPath) and fileExists(nimFile):
|
||||
if getLastModificationTime(depsPath) > getLastModificationTime(nimFile):
|
||||
return true # Already up-to-date
|
||||
|
||||
# Create output directory if needed
|
||||
createDir(parentDir(depsPath))
|
||||
|
||||
# Run nifler deps
|
||||
let cmd = quoteShell(c.nifler) & " deps " & quoteShell(nimFile) & " " & quoteShell(depsPath)
|
||||
let exitCode = execShellCmd(cmd)
|
||||
result = exitCode == 0
|
||||
|
||||
proc resolveFile(c: DepContext; origin, toResolve: string): string =
|
||||
## Resolve an import path relative to origin file
|
||||
# Handle std/ prefix
|
||||
var path = toResolve
|
||||
if path.startsWith("std/"):
|
||||
path = path.substr(4)
|
||||
|
||||
# Try relative to origin first
|
||||
let originDir = parentDir(origin)
|
||||
result = originDir / path.addFileExt("nim")
|
||||
if fileExists(result):
|
||||
return result
|
||||
|
||||
# Try search paths
|
||||
for searchPath in c.config.searchPaths:
|
||||
result = searchPath.string / path.addFileExt("nim")
|
||||
if fileExists(result):
|
||||
return result
|
||||
|
||||
result = ""
|
||||
|
||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
|
||||
|
||||
proc processInclude(c: var DepContext; includePath: string; current: Node) =
|
||||
let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
|
||||
if resolved.len == 0 or not fileExists(resolved):
|
||||
return
|
||||
|
||||
# Check for recursive includes
|
||||
for s in c.includeStack:
|
||||
if s == resolved:
|
||||
return # Skip recursive include
|
||||
|
||||
c.includeStack.add resolved
|
||||
current.files.add c.toPair(resolved)
|
||||
traverseDeps(c, c.toPair(resolved), current)
|
||||
discard c.includeStack.pop()
|
||||
|
||||
proc processImport(c: var DepContext; importPath: string; current: Node) =
|
||||
let resolved = resolveFile(c, current.files[0].nimFile, importPath)
|
||||
if resolved.len == 0 or not fileExists(resolved):
|
||||
return
|
||||
|
||||
let pair = c.toPair(resolved)
|
||||
let existingIdx = c.processedModules.getOrDefault(pair.modname, -1)
|
||||
|
||||
if existingIdx == -1:
|
||||
# New module - create node and process it
|
||||
let newNode = Node(files: @[pair], id: c.nodes.len)
|
||||
current.deps.add newNode.id
|
||||
c.processedModules[pair.modname] = newNode.id
|
||||
c.nodes.add newNode
|
||||
traverseDeps(c, pair, newNode)
|
||||
else:
|
||||
# Already processed - just add dependency
|
||||
if existingIdx notin current.deps:
|
||||
current.deps.add existingIdx
|
||||
|
||||
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
||||
## Read a .deps.nif file and process imports/includes
|
||||
let depsPath = c.depsFile(pair)
|
||||
if not fileExists(depsPath):
|
||||
return
|
||||
|
||||
var s = nifstreams.open(depsPath)
|
||||
defer: nifstreams.close(s)
|
||||
discard processDirectives(s.r)
|
||||
|
||||
var t = next(s)
|
||||
if t.kind != ParLe:
|
||||
return
|
||||
|
||||
# Skip to content (past stmts tag)
|
||||
t = next(s)
|
||||
|
||||
while t.kind != EofToken:
|
||||
if t.kind == ParLe:
|
||||
let tag = pool.tags[t.tagId]
|
||||
case tag
|
||||
of "import", "fromimport":
|
||||
# Read import path
|
||||
t = next(s)
|
||||
# Check for "when" marker (conditional import)
|
||||
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||
t = next(s) # skip it, still process the import
|
||||
# Handle path expression (could be ident, string, or infix like std/foo)
|
||||
var importPath = ""
|
||||
if t.kind == Ident:
|
||||
importPath = pool.strings[t.litId]
|
||||
elif t.kind == StringLit:
|
||||
importPath = pool.strings[t.litId]
|
||||
elif t.kind == ParLe and pool.tags[t.tagId] == "infix":
|
||||
# Handle std / foo style imports
|
||||
t = next(s) # skip infix tag
|
||||
if t.kind == Ident: # operator (/)
|
||||
t = next(s)
|
||||
if t.kind == Ident: # first part (std)
|
||||
importPath = pool.strings[t.litId]
|
||||
t = next(s)
|
||||
if t.kind == Ident: # second part (foo)
|
||||
importPath = importPath & "/" & pool.strings[t.litId]
|
||||
if importPath.len > 0:
|
||||
processImport(c, importPath, current)
|
||||
# Skip to end of import node
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
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
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
else:
|
||||
# Skip unknown node
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
t = next(s)
|
||||
|
||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) =
|
||||
## Process a module: run nifler and read deps
|
||||
if not runNifler(c, pair.nimFile):
|
||||
rawMessage(c.config, errGenerated, "nifler failed for: " & pair.nimFile)
|
||||
return
|
||||
readDepsFile(c, pair, current)
|
||||
|
||||
proc generateBuildFile(c: DepContext): string =
|
||||
## Generate the .build.nif file for nifmake
|
||||
result = getNimcacheDir(c.config).string / c.nodes[0].files[0].modname & ".build.nif"
|
||||
|
||||
var b = nifbuilder.open(result)
|
||||
defer: b.close()
|
||||
|
||||
b.addHeader("nim deps", "nifmake")
|
||||
b.addTree "stmts"
|
||||
|
||||
# Define nifler command
|
||||
b.addTree "cmd"
|
||||
b.addSymbolDef "nifler"
|
||||
b.addStrLit c.nifler
|
||||
b.addStrLit "parse"
|
||||
b.addStrLit "--deps"
|
||||
b.addTree "input"
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Define nim m command
|
||||
b.addTree "cmd"
|
||||
b.addSymbolDef "nim_m"
|
||||
b.addStrLit getAppFilename()
|
||||
b.addStrLit "m"
|
||||
# Add search paths
|
||||
for p in c.config.searchPaths:
|
||||
b.addStrLit "--path:" & p.string
|
||||
b.addTree "input"
|
||||
b.addIntLit 0
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Build rules for parsing (nifler)
|
||||
var seenFiles = initHashSet[string]()
|
||||
for node in c.nodes:
|
||||
for pair in node.files:
|
||||
let parsed = c.parsedFile(pair)
|
||||
if not seenFiles.containsOrIncl(parsed):
|
||||
b.addTree "do"
|
||||
b.addIdent "nifler"
|
||||
b.addTree "input"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit parsed
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit c.depsFile(pair)
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Build rules for semantic checking (nim m)
|
||||
for i in countdown(c.nodes.len - 1, 0):
|
||||
let node = c.nodes[i]
|
||||
let pair = node.files[0]
|
||||
b.addTree "do"
|
||||
b.addIdent "nim_m"
|
||||
# Input: all parsed files for this module
|
||||
for f in node.files:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.parsedFile(f)
|
||||
b.endTree()
|
||||
# Also depend on semmed files of dependencies
|
||||
for depIdx in node.deps:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.semmedFile(c.nodes[depIdx].files[0])
|
||||
b.endTree()
|
||||
# Output: semmed file
|
||||
b.addTree "output"
|
||||
b.addStrLit c.semmedFile(pair)
|
||||
b.endTree()
|
||||
b.addTree "args"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
b.endTree() # stmts
|
||||
|
||||
proc commandDeps*(conf: ConfigRef) =
|
||||
## Main entry point for `nim deps`
|
||||
when not defined(nimKochBootstrap):
|
||||
let nifler = findNifler()
|
||||
if nifler.len == 0:
|
||||
rawMessage(conf, errGenerated, "nifler tool not found. Install nimony or add nifler to PATH.")
|
||||
return
|
||||
|
||||
let projectFile = conf.projectFull.string
|
||||
if not fileExists(projectFile):
|
||||
rawMessage(conf, errGenerated, "project file not found: " & projectFile)
|
||||
return
|
||||
|
||||
# Create nimcache directory
|
||||
createDir(getNimcacheDir(conf).string)
|
||||
|
||||
var c = DepContext(
|
||||
config: conf,
|
||||
nifler: nifler,
|
||||
nodes: @[],
|
||||
processedModules: initTable[string, int](),
|
||||
includeStack: @[]
|
||||
)
|
||||
|
||||
# Create root node for main project file
|
||||
let rootPair = c.toPair(projectFile)
|
||||
let rootNode = Node(files: @[rootPair], id: 0)
|
||||
c.nodes.add rootNode
|
||||
c.processedModules[rootPair.modname] = 0
|
||||
|
||||
# Process dependencies
|
||||
traverseDeps(c, rootPair, rootNode)
|
||||
|
||||
# Generate build file
|
||||
let buildFile = generateBuildFile(c)
|
||||
rawMessage(conf, hintSuccess, "generated: " & buildFile)
|
||||
rawMessage(conf, hintSuccess, "run: nifmake run " & buildFile)
|
||||
else:
|
||||
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
|
||||
@@ -433,6 +433,9 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||
|
||||
proc genComment(d: PDoc, n: PNode): PRstNode =
|
||||
if n.comment.len > 0:
|
||||
if optDocRaw in d.conf.globalOptions:
|
||||
return newRstLeaf(n.comment)
|
||||
|
||||
d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
|
||||
try:
|
||||
result = parseRst(n.comment,
|
||||
@@ -1176,8 +1179,12 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
|
||||
"col": %n.info.col}
|
||||
)
|
||||
if comm != nil:
|
||||
result.rst = comm
|
||||
result.rstField = "description"
|
||||
if optDocRaw in d.conf.globalOptions:
|
||||
result.json["description"] = %comm.text
|
||||
else:
|
||||
result.rst = comm
|
||||
result.rstField = "description"
|
||||
|
||||
if r.buf.len > 0:
|
||||
result.json["code"] = %r.buf
|
||||
if k in routineKinds:
|
||||
@@ -1418,7 +1425,7 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
|
||||
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
|
||||
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
|
||||
of nkCallKinds:
|
||||
var comm: ItemPre = default(ItemPre)
|
||||
var comm = default(ItemPre)
|
||||
getAllRunnableExamples(d, n, comm)
|
||||
if comm.len != 0: d.modDescPre.add(comm)
|
||||
else: discard
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode
|
||||
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode
|
||||
|
||||
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||
internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
|
||||
@@ -61,10 +61,11 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||
# activate this pattern again:
|
||||
c.patterns[i] = pattern
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode =
|
||||
inc(c.hloLoopDetector)
|
||||
proc hlo(c: PContext, n: PNode, loopDetector: int): PNode =
|
||||
# simply stop and do not perform any further transformations:
|
||||
if c.hloLoopDetector > 300: return n
|
||||
if loopDetector > 300:
|
||||
message(c.config, n.info, warnUser, "term rewrite macro instantiation too nested")
|
||||
return n
|
||||
case n.kind
|
||||
of nkMacroDef, nkTemplateDef, procDefs:
|
||||
# already processed (special cases in semstmts.nim)
|
||||
@@ -80,7 +81,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
# no optimization applied, try subtrees:
|
||||
for i in 0..<result.safeLen:
|
||||
let a = result[i]
|
||||
let h = hlo(c, a)
|
||||
let h = hlo(c, a, loopDetector)
|
||||
if h != a: result[i] = h
|
||||
else:
|
||||
# perform type checking, so that the replacement still fits:
|
||||
@@ -90,17 +91,15 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
result = fitNode(c, n.typ, result, n.info)
|
||||
# optimization has been applied so check again:
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
result = hlo(c, result)
|
||||
result = hlo(c, result, loopDetector + 1)
|
||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
||||
|
||||
proc hloBody(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
||||
c.hloLoopDetector = 0
|
||||
result = hlo(c, n)
|
||||
result = hlo(c, n, 0)
|
||||
|
||||
proc hloStmt(c: PContext, n: PNode): PNode =
|
||||
# fast exit:
|
||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
||||
c.hloLoopDetector = 0
|
||||
result = hlo(c, n)
|
||||
result = hlo(c, n, 0)
|
||||
|
||||
@@ -968,10 +968,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
{sfPure, sfGlobal} <= v.sym.flags and
|
||||
isInProc
|
||||
|
||||
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
if isGlobalPragma:
|
||||
c.graph.procGlobals.add value
|
||||
c.graph.procGlobals.add newTree(nkFastAsgn, v, ri)
|
||||
else:
|
||||
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
result.add value
|
||||
elif ri.kind == nkEmpty and c.inLoop > 0:
|
||||
let skipInit = v.kind == nkDotExpr and # Closure var
|
||||
|
||||
@@ -728,44 +728,47 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mShrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> (BigInt($2) & 63n))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
applyFormat("($1 >> (BigInt($2) & 63n))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyInt..tyInt32}:
|
||||
let trimmerU = unsignedTrimmer(typ.size)
|
||||
let trimmerS = signedTrimmer(typ.size)
|
||||
r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
|
||||
r.res = "((($1 $2) >>> ($3 & $5)) $4)" % [xLoc, trimmerU, yLoc, trimmerS, $bitmask]
|
||||
else:
|
||||
applyFormat("($1 >>> $2)")
|
||||
r.res = "($1 >>> ($2 & $3))" % [xLoc, yLoc, $bitmask]
|
||||
of mShlI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
|
||||
applyFormat("BigInt.asIntN(64, $1 << (BigInt($2) & 63n))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
|
||||
applyFormat("BigInt.asUintN(64, $1 << (BigInt($2) & 63n))")
|
||||
else:
|
||||
applyFormat("($1 * Math.pow(2, $2))")
|
||||
applyFormat("($1 * Math.pow(2, ($2 & 63)))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
let trimmer = unsignedTrimmer(typ.size)
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
r.res = "(($1 << ($2 & $4)) $3)" % [xLoc, yLoc, trimmer, $bitmask]
|
||||
else:
|
||||
let trimmer = signedTrimmer(typ.size)
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
r.res = "(($1 << ($2 & $4)) $3)" % [xLoc, yLoc, trimmer, $bitmask]
|
||||
of mAshrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
applyFormat("($1 >> (BigInt($2) & 63n))")
|
||||
else:
|
||||
applyFormat("Math.floor($1 / Math.pow(2, $2))")
|
||||
applyFormat("Math.floor($1 / Math.pow(2, ($2 & 63)))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
applyFormat("($1 >>> $2)")
|
||||
r.res = "($1 >>> ($2 & $3)))" % [xLoc, yLoc, $bitmask]
|
||||
else:
|
||||
applyFormat("($1 >> $2)")
|
||||
r.res = "($1 >> ($2 & $3))" % [xLoc, yLoc, $bitmask]
|
||||
of mBitandI: bitwiseExpr("&")
|
||||
of mBitorI: bitwiseExpr("|")
|
||||
of mBitxorI: bitwiseExpr("^")
|
||||
@@ -1371,7 +1374,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.typ = etyBaseIndex
|
||||
let b = if n.kind == nkHiddenAddr: n[0] else: n
|
||||
gen(p, b[0], a)
|
||||
if skipTypes(b[0].typ, abstractVarRange).kind == tyTuple:
|
||||
if skipTypes(b[0].typ, abstractVarRange + tyTypeClasses).kind == tyTuple:
|
||||
# ref #25227 about `+ tyTypeClasses`
|
||||
r.res = makeJSString("Field" & $getFieldPosition(p, b[1]))
|
||||
else:
|
||||
if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
|
||||
@@ -2333,8 +2337,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" %
|
||||
[b, lhs.rdLoc, tmp]
|
||||
else:
|
||||
let (a, tmp) = maybeMakeTemp(p, n[1], lhs)
|
||||
r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
|
||||
useMagic(p, "nimAddStrStr")
|
||||
r.res = "nimAddStrStr($1, $2);" % [lhs.rdLoc, rhs.rdLoc]
|
||||
r.kind = resExpr
|
||||
of mAppendSeqElem:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
@@ -2438,7 +2442,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
binaryExpr(p, n, r, "mnewString",
|
||||
"""if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
|
||||
else {$3.length = $4; }""")
|
||||
of mSetLengthSeq:
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
var x, y: TCompRes = default(TCompRes)
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import std/[tables]
|
||||
import ast, astalgo
|
||||
|
||||
type
|
||||
|
||||
@@ -316,6 +316,28 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||
L.bufpos = msgPos
|
||||
lexMessage(L, msgKind, msg % t.literal)
|
||||
|
||||
proc checkBitWidth(L: var Lexer, base: NumericalBase, tokType: TokType,
|
||||
numDigits: int, startpos: int) =
|
||||
# Check bit width for non-base-10 literals
|
||||
# Warn if the digit count exceeds what can fit in the target type
|
||||
let bitsPerDigit = case base
|
||||
of base2: 1
|
||||
of base8: 3
|
||||
of base16: 4
|
||||
else: raiseAssert "unreachable"
|
||||
let bitWidth = case tokType
|
||||
of tkInt8Lit, tkUInt8Lit: 8
|
||||
of tkInt16Lit, tkUInt16Lit: 16
|
||||
of tkInt32Lit, tkUInt32Lit: 32
|
||||
of tkInt64Lit, tkUIntLit, tkIntLit, tkUInt64Lit: 64
|
||||
else: raiseAssert "unreachable"
|
||||
# Maximum digits = ceil(bitWidth / bitsPerDigit) = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
|
||||
let maxDigits = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
|
||||
if numDigits > maxDigits:
|
||||
lexMessageLitNum(L,
|
||||
"number has " & $numDigits & " digits but type only supports " &
|
||||
$maxDigits & " digits: '$1'", startpos, warnLongLiterals)
|
||||
|
||||
var
|
||||
xi: BiggestInt
|
||||
isBase10 = true
|
||||
@@ -491,6 +513,11 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
|
||||
else: internalError(L.config, getLineInfo(L), "getNumber")
|
||||
|
||||
# Check bit width for non-base-10 literals
|
||||
# Warn if the digit count exceeds what can fit in the target type
|
||||
if result.base != base10 and result.tokType in {tkIntLit..tkUInt64Lit} and numDigits > 0:
|
||||
checkBitWidth(L, result.base, result.tokType, numDigits, startpos)
|
||||
|
||||
# Bounds checks. Non decimal literals are allowed to overflow the range of
|
||||
# the datatype as long as their pattern don't overflow _bitwise_, hence
|
||||
# below checks of signed sizes against uint*.high is deliberate:
|
||||
@@ -896,7 +923,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||
tok.tokType = tkSymbol
|
||||
else:
|
||||
tok.tokType = TokType(tok.ident.id + ord(tkSymbol))
|
||||
if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
|
||||
if suspicious and {optStyleHint, optStyleError, optStyleWarning} * L.config.globalOptions != {}:
|
||||
lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
|
||||
L.bufpos = pos
|
||||
|
||||
|
||||
@@ -380,6 +380,10 @@ proc requiresDestructor(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
||||
if c.c != nil and typeInst != nil:
|
||||
result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
|
||||
elif typeInst != nil and getAttachedOp(c.g, typeInst, c.kind) != nil:
|
||||
# c.c == nil in lambdalifting
|
||||
# hooks are already insted
|
||||
result = getAttachedOp(c.g, typeInst, c.kind)
|
||||
else:
|
||||
localError(c.g.config, c.info,
|
||||
"cannot generate destructor for generic type: " & typeToString(t))
|
||||
@@ -1206,8 +1210,10 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
else:
|
||||
var tk: TTypeKind
|
||||
var skipped: PType = nil
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
|
||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||
skipped = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink})
|
||||
tk = skipped.kind
|
||||
else:
|
||||
tk = tyNone # no special casing for strings and seqs
|
||||
case tk
|
||||
@@ -1217,7 +1223,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
||||
else:
|
||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(skipped):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
|
||||
@@ -93,10 +93,12 @@ type
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnStdPrefix = "StdPrefix"
|
||||
warnUnknownNotes = "UnknownNotes"
|
||||
warnStdPrefix = "StdPrefix",
|
||||
warnUnknownNotes = "UnknownNotes",
|
||||
warnLongLiterals = "LongLiterals",
|
||||
warnUser = "User",
|
||||
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
|
||||
warnImplicitRangeConversion = "ImplicitRangeConversion",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
hintCC = "CC",
|
||||
@@ -202,8 +204,10 @@ const
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnStdPrefix: "$1 needs the 'std' prefix",
|
||||
warnUnknownNotes: "$1",
|
||||
warnLongLiterals: "$1",
|
||||
warnUser: "$1",
|
||||
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
|
||||
warnImplicitRangeConversion: "implicit range conversion $1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
||||
@@ -258,7 +262,7 @@ type
|
||||
|
||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
result = default(array[0..3, TNoteKinds])
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix, warnImplicitRangeConversion}
|
||||
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
|
||||
result[1] = result[2] - {warnProveField, warnProveIndex,
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
|
||||
@@ -95,7 +95,7 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
|
||||
## Check symbol definitions adhere to NEP1 style rules.
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
{optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # check only if hint/error/warning is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
@@ -136,7 +136,7 @@ proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
|
||||
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
@@ -152,7 +152,7 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
|
||||
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
|
||||
## Check builtin pragma uses match their definition's style.
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
|
||||
import std/[intsets, tables, hashes, strtabs, os, strutils, parseutils]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
|
||||
import ic / [packed_ast, ic]
|
||||
|
||||
@@ -648,7 +648,9 @@ template internalAssert*(conf: ConfigRef, e: bool) =
|
||||
|
||||
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
|
||||
let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
|
||||
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
|
||||
let msg = if optStyleError in conf.globalOptions: errGenerated
|
||||
elif optStyleWarning in conf.globalOptions: warnUser
|
||||
else: hintName
|
||||
liMessage(conf, info, msg, m, doNothing, instLoc())
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
|
||||
|
||||
@@ -25,7 +25,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2025"
|
||||
copyrightYear* = "2026"
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
@@ -68,6 +68,7 @@ type # please make sure we have under 32 options
|
||||
optUseNimcache, # save artifacts (including binary) in $nimcache
|
||||
optStyleHint, # check that the names adhere to NEP-1
|
||||
optStyleError, # enforce that the names adhere to NEP-1
|
||||
optStyleWarning, # emit style checks as warnings
|
||||
optStyleUsages, # only enforce consistent **usages** of the symbol
|
||||
optSkipSystemConfigFile, # skip the system's cfg/nims config file
|
||||
optSkipProjConfigFile, # skip the project's cfg/nims config file
|
||||
@@ -110,6 +111,8 @@ type # please make sure we have under 32 options
|
||||
optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types.
|
||||
optShowNonExportedFields # for documentation: show fields that are not exported
|
||||
optJsBigInt64 # use bigints for 64-bit integers in JS
|
||||
optDocRaw # for documentation: Don't render markdown for JSON output
|
||||
optItaniumMangle # mangling follows the Itanium spec
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
@@ -1038,6 +1041,9 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
|
||||
proc showNonExportedFields*(conf: ConfigRef) =
|
||||
incl(conf.globalOptions, optShowNonExportedFields)
|
||||
|
||||
proc docRawOutput*(conf: ConfigRef) =
|
||||
incl(conf.globalOptions, optDocRaw)
|
||||
|
||||
proc expandDone*(conf: ConfigRef): bool =
|
||||
result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress
|
||||
|
||||
|
||||
@@ -830,6 +830,21 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
|
||||
for i in 0 ..< flatUnbound.len():
|
||||
x.bindings.put(flatUnbound[i], flatBound[i])
|
||||
|
||||
proc compactVoidArgs(n: PNode): PNode =
|
||||
# deletes void args from the argument list, which are created by `setSon`
|
||||
var hasNil = false
|
||||
for i in 0..<n.len:
|
||||
if n[i] == nil:
|
||||
hasNil = true
|
||||
break
|
||||
if not hasNil:
|
||||
result = n
|
||||
else:
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
if n[i] != nil:
|
||||
result.add n[i]
|
||||
|
||||
proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
n: PNode, flags: TExprFlags;
|
||||
expectedType: PType = nil): PNode =
|
||||
@@ -880,7 +895,7 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
markUsed(c, info, finalCallee, isGenericInstance = true)
|
||||
onUse(info, finalCallee, isGenericInstance = true)
|
||||
|
||||
result = x.call
|
||||
result = compactVoidArgs(x.call)
|
||||
instGenericConvertersSons(c, result, x)
|
||||
markConvertersUsed(c, result)
|
||||
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
|
||||
|
||||
@@ -153,7 +153,6 @@ type
|
||||
generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
|
||||
topStmts*: int # counts the number of encountered top level statements
|
||||
lastGenericIdx*: int # used for the generics stack
|
||||
hloLoopDetector*: int # used to prevent endless loops in the HLO
|
||||
inParallelStmt*: int
|
||||
instTypeBoundOp*: proc (c: PContext; dc: PSym; t: PType; info: TLineInfo;
|
||||
op: TTypeAttachedOp; col: int): PSym {.nimcall.}
|
||||
@@ -708,7 +707,7 @@ proc analyseIfAddressTakenInCall*(c: PContext, n: PNode, isConverter = false) =
|
||||
return
|
||||
const
|
||||
FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
|
||||
mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
|
||||
mSetLengthStr, mSetLengthSeq, mSetLengthSeqUninit, mAppendStrCh, mAppendStrStr, mSwap,
|
||||
mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove, mWasMoved}
|
||||
|
||||
template checkIfConverterCalled(c: PContext, n: PNode) =
|
||||
|
||||
@@ -813,7 +813,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
inc(lastIndex)
|
||||
if isGeneric:
|
||||
for i in 0..<result.len:
|
||||
if isIntLit(result[i].typ):
|
||||
if result[i].typ != nil and isIntLit(result[i].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
result[i].typ() = nil
|
||||
result.typ() = nil # current result.typ is invalid, index type is nil
|
||||
@@ -832,9 +832,6 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
proc fixAbstractType(c: PContext, n: PNode) =
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
if it == nil:
|
||||
localError(c.config, n.info, "'$1' has nil child at index $2" % [renderTree(n, {renderNoComments}), $i])
|
||||
return
|
||||
# do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
|
||||
if it.kind == nkHiddenSubConv and
|
||||
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
|
||||
@@ -1155,7 +1152,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
|
||||
localError(c.config, n.info, msg)
|
||||
return errorNode(c, n)
|
||||
else:
|
||||
result = m.call
|
||||
result = compactVoidArgs(m.call)
|
||||
instGenericConvertersSons(c, result, m)
|
||||
markConvertersUsed(c, result)
|
||||
|
||||
@@ -1661,6 +1658,9 @@ proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
n[0] = a
|
||||
result = n
|
||||
var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
|
||||
if t.kind == tyTypeDesc:
|
||||
localError(c.config, n.info, "missing generic parameter")
|
||||
return nil
|
||||
case t.kind
|
||||
of tyRef, tyPtr: n.typ() = t.elementType
|
||||
of tyMetaTypes, tyFromExpr:
|
||||
@@ -2799,7 +2799,7 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||
expectedElementType = typ
|
||||
if isGeneric:
|
||||
for i in 0..<n.len:
|
||||
if isIntLit(n[i].typ):
|
||||
if n[i].typ != nil and isIntLit(n[i].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
n[i].typ() = nil
|
||||
result.add n[i]
|
||||
@@ -2912,7 +2912,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
|
||||
result.add n[i]
|
||||
if isGeneric:
|
||||
for i in 0..<result.len:
|
||||
if isIntLit(result[i][1].typ):
|
||||
if result[i][1].typ != nil and isIntLit(result[i][1].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
result[i][1].typ() = nil
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
@@ -2953,7 +2953,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
|
||||
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
|
||||
if isGeneric:
|
||||
for i in 0..<result.len:
|
||||
if isIntLit(result[i].typ):
|
||||
if result[i].typ != nil and isIntLit(result[i].typ):
|
||||
# generic instantiation strips int lit type which makes conversions fail
|
||||
result[i].typ() = nil
|
||||
result.typ() = makeTypeFromExpr(c, result.copyTree)
|
||||
@@ -3053,6 +3053,13 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
|
||||
s = nextOverloadIter(o, c, a)
|
||||
|
||||
proc isTypeTupleField(n: PNode): bool {.inline.} =
|
||||
result = n.typ.kind == tyTypeDesc or
|
||||
(n.typ.kind == tyGenericParam and n.typ.sym.kind == skGenericParam)
|
||||
# `skGenericParam` stays as `tyGenericParam` type rather than being wrapped in `tyTypeDesc`
|
||||
# would check if `n` itself is an `skGenericParam` symbol, but these symbols semcheck to an ident
|
||||
# maybe check if `n` is an ident to ensure this is not a value with the generic param type?
|
||||
|
||||
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
result = semTuplePositionsConstr(c, n, flags, expectedType)
|
||||
if result.typ.kind == tyFromExpr:
|
||||
@@ -3063,10 +3070,10 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||
var isTupleType: bool = false
|
||||
if tupexp.len > 0: # don't interpret () as type
|
||||
internalAssert c.config, tupexp.kind == nkTupleConstr
|
||||
isTupleType = tupexp[0].typ.kind == tyTypeDesc
|
||||
isTupleType = isTypeTupleField(tupexp[0])
|
||||
# check if either everything or nothing is tyTypeDesc
|
||||
for i in 1..<tupexp.len:
|
||||
if isTupleType != (tupexp[i].typ.kind == tyTypeDesc):
|
||||
if isTupleType != isTypeTupleField(tupexp[i]):
|
||||
return localErrorNode(c, n, tupexp[i].info, "Mixing types and values in tuples is not allowed.")
|
||||
if isTupleType: # expressions as ``(int, string)`` are reinterpret as type expressions
|
||||
result = n
|
||||
|
||||
@@ -16,7 +16,7 @@ import
|
||||
commands, magicsys, modulegraphs, lineinfos, wordrecg
|
||||
|
||||
import std/[strutils, math, strtabs]
|
||||
from system/memory import nimCStrLen
|
||||
#from system/memory import nimCStrLen
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
@@ -179,29 +179,30 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
let argB = getInt(b)
|
||||
result = newIntNodeT(if argA > argB: argA else: argB, n, idgen, g)
|
||||
of mShlI:
|
||||
let valueB = toInt64(getInt(b)) and (n.typ.size * 8 - 1)
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for shl")
|
||||
of mShrI:
|
||||
var a = cast[uint64](getInt(a))
|
||||
let b = cast[uint64](getInt(b))
|
||||
let b = cast[uint64](getInt(b)) and cast[uint64](n.typ.size * 8 - 1)
|
||||
# To support the ``-d:nimOldShiftRight`` flag, we need to mask the
|
||||
# signed integers to cut off the extended sign bit in the internal
|
||||
# representation.
|
||||
@@ -220,12 +221,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
let c = cast[BiggestInt](a shr b)
|
||||
result = newIntNodeT(toInt128(c), n, idgen, g)
|
||||
of mAshrI:
|
||||
let valueB = toInt64(getInt(b)) and (n.typ.size * 8 - 1)
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, idgen, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt64, tyInt:
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), valueB)), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for ashr")
|
||||
of mDivI:
|
||||
let argA = getInt(a)
|
||||
|
||||
@@ -243,7 +243,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
let cond = operand.kind == tyTuple and operand.n != nil
|
||||
result = newIntNodeT(toInt128(ord(cond)), traitCall, c.idgen, c.graph)
|
||||
of "tupleLen":
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
var operand = operand.skipTypes({tyGenericInst, tyAlias})
|
||||
assert operand.kind == tyTuple, $operand.kind
|
||||
result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
|
||||
of "distinctBase":
|
||||
@@ -667,7 +667,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
result = semQuantifier(c, n)
|
||||
of mOld:
|
||||
result = semOld(c, n)
|
||||
of mSetLengthSeq:
|
||||
of mSetLengthSeq, mSetLengthSeqUninit:
|
||||
result = n
|
||||
let seqType = result[1].typ.skipTypes({tyPtr, tyRef, # in case we had auto-dereferencing
|
||||
tyVar, tyGenericInst, tyOwned, tySink,
|
||||
|
||||
@@ -11,7 +11,7 @@ import
|
||||
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
wordrecg, options, guards, lineinfos, semfold, semdata,
|
||||
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
|
||||
semstrictfuncs, suggestsymdb, pushpoppragmas, lowerings
|
||||
semstrictfuncs, suggestsymdb, pushpoppragmas
|
||||
|
||||
import std/[tables, intsets, strutils, sequtils]
|
||||
|
||||
@@ -84,6 +84,8 @@ type
|
||||
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
isInnerProc: bool
|
||||
inEnforcedNoSideEffects: bool
|
||||
isArrayIndexing: bool
|
||||
currentExceptType: PType
|
||||
unknownRaises: seq[(PSym, TLineInfo)]
|
||||
currOptions: TOptions
|
||||
optionsStack: seq[(TOptions, TNoteKinds)]
|
||||
@@ -146,6 +148,37 @@ proc isLocalSym(a: PEffects, s: PSym): bool =
|
||||
s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
|
||||
sfGlobal notin s.flags and s.owner == a.owner
|
||||
|
||||
proc isRangeSupertype(conf: ConfigRef; wider, narrower: PType): bool =
|
||||
## Check if `wider` type fully contains `narrower` type
|
||||
## Returns true if narrower fits entirely within wider (safe conversion)
|
||||
if wider.isOrdinalType:
|
||||
let wideFirst = firstOrd(conf, wider)
|
||||
let wideLast = lastOrd(conf, wider)
|
||||
let narrowFirst = firstOrd(conf, narrower)
|
||||
let narrowLast = lastOrd(conf, narrower)
|
||||
result = narrowFirst >= wideFirst and narrowLast <= wideLast
|
||||
elif not narrower.isOrdinalType:
|
||||
let wideFirst = firstFloat(wider)
|
||||
let wideLast = lastFloat(wider)
|
||||
let narrowFirst = firstFloat(narrower)
|
||||
let narrowLast = lastFloat(narrower)
|
||||
result = narrowFirst >= wideFirst and narrowLast <= wideLast
|
||||
else:
|
||||
# int -> float ranges; warn
|
||||
result = false
|
||||
|
||||
proc shouldWarnRangeConversion(conf: ConfigRef; formalType, argType: PType): bool =
|
||||
## Determine if an implicit range conversion should warn
|
||||
## We warn on conversions that are likely to cause panics
|
||||
let f = formalType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
let a = argType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if f.kind == tyRange:
|
||||
# Only warn if formal range doesn't fully contain argument range
|
||||
# Check if the ranges don't perfectly overlap
|
||||
result = not isRangeSupertype(conf, f, a)
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc lockLocations(a: PEffects; pragma: PNode) =
|
||||
if pragma.kind != nkExprColonExpr:
|
||||
localError(a.config, pragma.info, "locks pragma without argument")
|
||||
@@ -576,11 +609,25 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
|
||||
let b = n[i]
|
||||
if b.kind == nkExceptBranch:
|
||||
setLen(tracked.init, oldState)
|
||||
# If this except branch catches exactly one type, record it so an
|
||||
# empty `raise` inside the branch can be inferred as re-raising that
|
||||
# specific exception type instead of the generic `Exception`.
|
||||
var savedExcept: PType = tracked.currentExceptType
|
||||
var inferredExcept: PType = nil
|
||||
if b.len == 2:
|
||||
if b[0].isInfixAs():
|
||||
assert(b[0][1].kind == nkType)
|
||||
inferredExcept = b[0][1].typ
|
||||
else:
|
||||
assert(b[0].kind == nkType)
|
||||
inferredExcept = b[0].typ
|
||||
tracked.currentExceptType = inferredExcept
|
||||
for j in 0..<b.len - 1:
|
||||
if b[j].isInfixAs(): # skips initialization checks
|
||||
assert(b[j][2].kind == nkSym)
|
||||
tracked.init.add b[j][2].sym.id
|
||||
track(tracked, b[^1])
|
||||
tracked.currentExceptType = savedExcept
|
||||
for i in oldState..<tracked.init.len:
|
||||
addToIntersection(inter, tracked.init[i], bsNone)
|
||||
else:
|
||||
@@ -1263,7 +1310,14 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
# A `raise` with no arguments means we're going to re-raise the exception
|
||||
# being handled or, if outside of an `except` block, a `ReraiseDefect`.
|
||||
# Here we add a `Exception` tag in order to cover both the cases.
|
||||
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
if tracked.currentExceptType != nil:
|
||||
var en = newNode(nkType)
|
||||
en.typ = tracked.currentExceptType
|
||||
en.info = n.info
|
||||
addRaiseEffect(tracked, en, nil)
|
||||
createTypeBoundOps(tracked, tracked.currentExceptType, n.info)
|
||||
else:
|
||||
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
|
||||
of nkCallKinds:
|
||||
trackCall(tracked, n)
|
||||
of nkDotExpr:
|
||||
@@ -1481,6 +1535,11 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
message(tracked.config, n.info, warnPtrToCstringConv,
|
||||
$n[1].typ)
|
||||
|
||||
# Check for implicit range conversions
|
||||
if n.kind == nkHiddenStdConv and (not tracked.isArrayIndexing) and
|
||||
shouldWarnRangeConversion(tracked.config, n.typ, n[1].typ):
|
||||
message(tracked.config, n.info, warnImplicitRangeConversion,
|
||||
typeToString(n[1].typ) & " -> " & typeToString(n.typ))
|
||||
|
||||
let t = n.typ.skipTypes(abstractInst)
|
||||
if t.kind == tyEnum:
|
||||
@@ -1519,7 +1578,12 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
checkBounds(tracked, n[0], n[1])
|
||||
track(tracked, n[0])
|
||||
dec tracked.leftPartOfAsgn
|
||||
for i in 1 ..< n.len: track(tracked, n[i])
|
||||
for i in 1 ..< n.len:
|
||||
if i == 1:
|
||||
tracked.isArrayIndexing = true
|
||||
track(tracked, n[i])
|
||||
if i == 1:
|
||||
tracked.isArrayIndexing = false
|
||||
inc tracked.leftPartOfAsgn
|
||||
of nkError:
|
||||
localError(tracked.config, n.info, errorToString(tracked.config, n))
|
||||
|
||||
@@ -394,8 +394,9 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||
elif a.len == 1:
|
||||
# count number of ``except: body`` blocks
|
||||
inc catchAllExcepts
|
||||
message(c.config, a.info, warnBareExcept,
|
||||
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||
if noPanicOnExcept in c.graph.config.legacyFeatures:
|
||||
message(c.config, a.info, warnBareExcept,
|
||||
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||
else:
|
||||
# support ``except KeyError, ValueError, ... : body``
|
||||
if catchAllExcepts > 0:
|
||||
@@ -616,7 +617,7 @@ proc checkDefineType(c: PContext; v: PSym; t: PType) =
|
||||
# no distinct types for generic define
|
||||
skipped.excl tyDistinct
|
||||
if t.skipTypes(skipped).kind notin typeKinds:
|
||||
let name =
|
||||
let name =
|
||||
case v.magic
|
||||
of mStrDefine: "strdefine"
|
||||
of mIntDefine: "intdefine"
|
||||
@@ -1828,6 +1829,13 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
x[0].kind in {nkObjectTy, nkTupleTy})
|
||||
):
|
||||
checkForMetaFields(c, baseType.n, hasError)
|
||||
|
||||
if s.typ.kind in {tySet, tyArray, tySequence, tyUncheckedArray} and s.typ.elementType.kind == tyNone:
|
||||
# magic generics are not filled but tyNone is added to its elements by default,
|
||||
# we lift them to tyBuiltInTypeClass here
|
||||
s.typ = newTypeS(tyBuiltInTypeClass, c,
|
||||
newTypeS(s.typ.kind, c))
|
||||
|
||||
if not hasError:
|
||||
checkConstructedType(c.config, s.info, s.typ)
|
||||
#instAllTypeBoundOp(c, n.info)
|
||||
@@ -2639,7 +2647,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
# we need to add a result symbol for them
|
||||
maybeAddResult(c, s, n)
|
||||
|
||||
|
||||
|
||||
trackProc(c, s, s.ast[bodyPos])
|
||||
else:
|
||||
if (s.typ.returnType != nil and s.kind != skIterator):
|
||||
|
||||
@@ -533,7 +533,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
typ = a[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, a[^1], typ)
|
||||
typ = a[^1].typ
|
||||
typ = a[^1].typ.skipIntLit(c.idgen)
|
||||
elif a[^2].kind != nkEmpty:
|
||||
typ = semTypeNode(c, a[^2], nil)
|
||||
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
|
||||
@@ -905,7 +905,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
typ = n[^1].typ
|
||||
else:
|
||||
fitDefaultNode(c, n[^1], typ)
|
||||
typ = n[^1].typ
|
||||
typ = n[^1].typ.skipIntLit(c.idgen)
|
||||
propagateToOwner(rectype, typ)
|
||||
elif n[^2].kind == nkEmpty:
|
||||
localError(c.config, n.info, errTypeExpected)
|
||||
@@ -986,7 +986,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
|
||||
proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
|
||||
obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
|
||||
if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
|
||||
(innerObj != nil and obj.sym.id == innerObj.sym.id):
|
||||
(innerObj != nil and obj.id == innerObj.id):
|
||||
localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
|
||||
result = false
|
||||
elif obj.kind == tyObject:
|
||||
@@ -1109,7 +1109,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
let t = newTypeS(tySink, c, result)
|
||||
result = t
|
||||
else: discard
|
||||
if result.kind == tyRef and
|
||||
if result.kind == tyRef and
|
||||
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
tfTriggersCompileTime notin result.flags:
|
||||
result.flags.incl tfHasAsgn
|
||||
@@ -2176,7 +2176,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
result = semTypeNode(c, whenResult, prev)
|
||||
of nkBracketExpr:
|
||||
checkMinSonsLen(n, 2, c.config)
|
||||
# Actually len >= 2 is required, but it doesn't print errors nicely with empty brackets
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
var head = n[0]
|
||||
var s = if head.kind notin nkCallKinds: semTypeIdent(c, head)
|
||||
else: symFromExpectedTypeNode(c, semExpr(c, head))
|
||||
@@ -2194,10 +2195,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
incl result.flags, tfHasAsgn
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
of mTypeDesc, mType, mTypeOf:
|
||||
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
result.flags.incl tfExplicit
|
||||
if n.len != 2:
|
||||
let name = case s.magic:
|
||||
of mTypeDesc: "typedesc"
|
||||
of mType: "type"
|
||||
of mTypeOf: "typeof"
|
||||
else: ""
|
||||
localError(c.config, n.info, errXExpectsOneTypeParam % name)
|
||||
else:
|
||||
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||
result.flags.incl tfExplicit
|
||||
of mStatic:
|
||||
result = semStaticType(c, n[1], prev)
|
||||
if n.len != 2:
|
||||
localError(c.config, n.info, errXExpectsOneTypeParam % "static")
|
||||
else:
|
||||
result = semStaticType(c, n[1], prev)
|
||||
of mExpr:
|
||||
result = semTypeNode(c, n[0], nil)
|
||||
if result != nil:
|
||||
@@ -2207,9 +2219,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
for i in 1..<n.len:
|
||||
result.rawAddSon(semTypeNode(c, n[i], nil))
|
||||
of mDistinct:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = newOrPrevType(tyDistinct, prev, c)
|
||||
addSonSkipIntLit(result, semTypeNode(c, n[1], nil), c.idgen)
|
||||
of mVar:
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = newOrPrevType(tyVar, prev, c)
|
||||
var base = semTypeNode(c, n[1], nil)
|
||||
if base.kind in {tyVar, tyLent}:
|
||||
|
||||
@@ -249,13 +249,24 @@ proc hasValuelessStatics(n: PNode): bool =
|
||||
a
|
||||
proc doThing(_: MyThing)
|
||||
]#
|
||||
result = false
|
||||
if n.safeLen == 0 and n.kind != nkEmpty: # Some empty nodes can get in here
|
||||
n.typ == nil or n.typ.kind == tyStatic
|
||||
if n.typ == nil:
|
||||
result = true
|
||||
elif n.typ.kind == tyStatic:
|
||||
result = true
|
||||
elif n.typ.kind == tyTypeDesc:
|
||||
# Check if the base type is an unresolved generic parameter.
|
||||
# This handles cases where a template containing sizeof(T) is called
|
||||
# inside a generic object's when clause - the T needs to be resolved
|
||||
# before we can evaluate the condition.
|
||||
let base = n.typ.skipTypes({tyTypeDesc})
|
||||
if base.kind == tyGenericParam:
|
||||
result = true
|
||||
else:
|
||||
for x in n:
|
||||
if hasValuelessStatics(x):
|
||||
return true
|
||||
false
|
||||
|
||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode =
|
||||
if n == nil: return
|
||||
@@ -280,7 +291,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PT
|
||||
(cl.owner == nil or result.sym.owner == cl.owner):
|
||||
# instantiate default value of object/tuple field
|
||||
cl.c.fitDefaultNode(cl.c, result.sym.ast, result.sym.typ)
|
||||
result.sym.typ = result.sym.ast.typ
|
||||
result.sym.typ = result.sym.ast.typ.skipIntLit(cl.c.idgen)
|
||||
# sym type can be nil if was gensym created by macro, see #24048
|
||||
if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
|
||||
# don't add the 'void' field
|
||||
|
||||
@@ -41,6 +41,7 @@ type
|
||||
CoType
|
||||
CoOwnerSig
|
||||
CoIgnoreRange
|
||||
CoIgnoreRangeInArray
|
||||
CoConsiderOwned
|
||||
CoDistinct
|
||||
CoHashTypeInsideNode
|
||||
@@ -150,7 +151,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
assert inst.kind == tyGenericInst
|
||||
c.hashType inst.genericHead, flags, conf
|
||||
for _, a in inst.genericInstParams:
|
||||
c.hashType a, flags, conf
|
||||
c.hashType a, flags+{CoDistinct}, conf
|
||||
t.typeInst = inst
|
||||
return
|
||||
c &= char(t.kind)
|
||||
@@ -216,10 +217,17 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
else:
|
||||
for a in t.kids: c.hashType a, flags+{CoIgnoreRange}, conf
|
||||
of tyRange:
|
||||
if CoIgnoreRange notin flags:
|
||||
if {CoIgnoreRange, CoIgnoreRangeInArray} * flags == {}:
|
||||
c &= char(t.kind)
|
||||
c.hashTree(t.n, {}, conf)
|
||||
c.hashType(t.elementType, flags, conf)
|
||||
c.hashType(t.elementType, flags, conf)
|
||||
elif CoIgnoreRangeInArray in flags:
|
||||
# include only the length of the range (not its specific bounds)
|
||||
c &= char(t.kind)
|
||||
let l = lengthOrd(conf, t)
|
||||
lowlevel l
|
||||
else:
|
||||
c.hashType(t.elementType, flags, conf)
|
||||
of tyStatic:
|
||||
c &= char(t.kind)
|
||||
c.hashTree(t.n, {}, conf)
|
||||
@@ -249,7 +257,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
if tfVarargs in t.flags: c &= ".varargs"
|
||||
of tyArray:
|
||||
c &= char(t.kind)
|
||||
c.hashType(t.indexType, flags-{CoIgnoreRange}, conf)
|
||||
c.hashType(t.indexType, flags-{CoIgnoreRange}+{CoIgnoreRangeInArray}, conf)
|
||||
c.hashType(t.elementType, flags-{CoIgnoreRange}, conf)
|
||||
else:
|
||||
c &= char(t.kind)
|
||||
@@ -434,4 +442,3 @@ proc idOrSig*(s: PSym, currentModule: string,
|
||||
if counter != 0:
|
||||
result.add "_" & rope(counter+1)
|
||||
sigCollisions.inc(sig)
|
||||
|
||||
|
||||
@@ -617,6 +617,8 @@ proc isGenericObjectOf(f, a: PType): bool =
|
||||
# use sym equality to check if the `tyGenericBody` types are equal
|
||||
result = aRoot != nil and f.sym == aRoot.sym
|
||||
|
||||
|
||||
|
||||
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
|
||||
var t = a
|
||||
assert t.kind == tyObject
|
||||
@@ -1677,7 +1679,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
elif a.kind == tyGenericInst:
|
||||
if roota.base == rootf.base:
|
||||
let nextFlags = flags + {trNoCovariance}
|
||||
var hasCovariance = false
|
||||
# YYYY
|
||||
result = isEqual
|
||||
|
||||
@@ -1689,7 +1690,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
if res notin {isEqual, isGeneric}:
|
||||
if trNoCovariance notin flags and ff.kind == aa.kind:
|
||||
let paramFlags = rootf.base[i-1].flags
|
||||
hasCovariance =
|
||||
let hasCovariance =
|
||||
if tfCovariant in paramFlags:
|
||||
if tfWeakCovariant in paramFlags:
|
||||
isCovariantPtr(c, ff, aa)
|
||||
@@ -1700,35 +1701,36 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
typeRel(c, aa, ff, flags) == isSubtype
|
||||
if hasCovariance:
|
||||
continue
|
||||
result = isNone
|
||||
break
|
||||
|
||||
return isNone
|
||||
if prev == nil: put(c, f, a)
|
||||
else:
|
||||
let fKind = rootf.last.kind
|
||||
if fKind in {tyAnd, tyOr}:
|
||||
result = typeRel(c, last(f), a, flags)
|
||||
if result != isNone: put(c, f, a)
|
||||
if result != isNone:
|
||||
if prev == nil: put(c, f, a)
|
||||
return
|
||||
|
||||
var aAsObject = roota.last
|
||||
let fKind = rootf.last.kind
|
||||
if fKind in {tyAnd, tyOr}:
|
||||
result = typeRel(c, last(f), a, flags)
|
||||
if result != isNone: put(c, f, a)
|
||||
return
|
||||
|
||||
if fKind in {tyRef, tyPtr}:
|
||||
if aAsObject.kind == tyObject:
|
||||
# bug #7600, tyObject cannot be passed
|
||||
# as argument to tyRef/tyPtr
|
||||
return isNone
|
||||
elif aAsObject.kind == fKind:
|
||||
aAsObject = aAsObject.base
|
||||
var aAsObject = roota.last
|
||||
|
||||
if aAsObject.kind == tyObject and trIsOutParam notin flags:
|
||||
let baseType = aAsObject.base
|
||||
if baseType != nil:
|
||||
if tfFinal notin aAsObject.flags:
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
let ret = typeRel(c, f, baseType, flags)
|
||||
return if ret in {isEqual,isGeneric}: isSubtype else: ret
|
||||
if fKind in {tyRef, tyPtr}:
|
||||
if aAsObject.kind == tyObject:
|
||||
# bug #7600, tyObject cannot be passed
|
||||
# as argument to tyRef/tyPtr
|
||||
return isNone
|
||||
elif aAsObject.kind == fKind:
|
||||
aAsObject = aAsObject.base
|
||||
|
||||
result = isNone
|
||||
if aAsObject.kind == tyObject and trIsOutParam notin flags:
|
||||
let baseType = aAsObject.base
|
||||
if baseType != nil:
|
||||
if tfFinal notin aAsObject.flags:
|
||||
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
|
||||
let ret = typeRel(c, f, baseType, flags)
|
||||
return if ret in {isEqual,isGeneric}: isSubtype else: ret
|
||||
else:
|
||||
assert last(origF) != nil
|
||||
result = typeRel(c, last(origF), a, flags)
|
||||
@@ -3093,6 +3095,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
put(m, formal.typ, defaultValue.typ)
|
||||
defaultValue.flags.incl nfDefaultParam
|
||||
setSon(m.call, formal.position + 1, defaultValue)
|
||||
|
||||
# forget all inferred types if the overload matching failed
|
||||
if m.state == csNoMatch:
|
||||
for t in m.inferredTypes:
|
||||
|
||||
@@ -828,8 +828,11 @@ proc transformFor(c: PTransf, n: PNode): PNode =
|
||||
elif t.destructor == nil and arg.typ.destructor != nil:
|
||||
t = arg.typ
|
||||
|
||||
if arg.kind in {nkDerefExpr, nkHiddenDeref}:
|
||||
if arg.kind in {nkDerefExpr, nkHiddenDeref} and
|
||||
arg[0].typ.skipTypes(abstractInst).kind != tyLent:
|
||||
# optimizes for `[]` # bug #24093
|
||||
# bug #25251: enforce a copy if the arg is a deref of a lent pointer
|
||||
# since the arg could be a temporary that will go out of scope
|
||||
var temp = newTemp(c, arg[0].typ, formal.info)
|
||||
addVar(v, temp)
|
||||
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg[0], true))
|
||||
|
||||
@@ -21,20 +21,13 @@ proc hashTree*(n: PNode): Hash =
|
||||
return
|
||||
result = ord(n.kind)
|
||||
case n.kind
|
||||
of nkEmpty, nkNilLit, nkType:
|
||||
discard
|
||||
of nkIdent:
|
||||
result = result !& n.ident.h
|
||||
of nkSym:
|
||||
result = result !& n.sym.id
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
|
||||
result = result !& int(n.intVal)
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
if (n.floatVal >= - 1000000.0) and (n.floatVal <= 1000000.0):
|
||||
result = result !& toInt(n.floatVal)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
result = result !& hash(n.strVal)
|
||||
of nkEmpty: discard
|
||||
of nkSym: result = result !& n.sym.id
|
||||
of nkIdent: result = result !& n.ident.h
|
||||
of nkCharLit..nkUInt64Lit: result = result !& hash(n.intVal)
|
||||
of nkFloatLit..nkFloat64Lit: result = result !& hash(cast[uint64](n.floatVal))
|
||||
of nkStrLit..nkTripleStrLit: result = result !& hash(n.strVal)
|
||||
of nkType, nkNilLit: result = result !& hash(n.typ.itemId)
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
result = result !& hashTree(n[i])
|
||||
@@ -53,7 +46,6 @@ proc treesEquivalent(a, b: PNode; ignoreTypes: bool): bool =
|
||||
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal)
|
||||
#result = a.floatVal == b.floatVal
|
||||
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
|
||||
of nkType, nkNilLit:
|
||||
result = a.typ == b.typ
|
||||
|
||||
@@ -165,7 +165,7 @@ proc isFloatLit*(t: PType): bool {.inline.} =
|
||||
|
||||
proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) =
|
||||
result.add typeToString(typ, prefer)
|
||||
if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym)
|
||||
if getDeclarationPath and typ.sym != nil: result.addDeclaredLoc(conf, typ.sym)
|
||||
|
||||
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName; getDeclarationPath = true): string =
|
||||
assert sym != nil
|
||||
|
||||
@@ -480,9 +480,9 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
|
||||
else:
|
||||
asgnComplex(dest, src)
|
||||
|
||||
proc compile(c: PCtx, s: PSym): int =
|
||||
proc compile(c: PCtx, s: PSym): VmProcInfo =
|
||||
result = vmgen.genProc(c, s)
|
||||
when debugEchoCode: c.echoCode result
|
||||
when debugEchoCode: c.echoCode result.pc
|
||||
#c.echoCode
|
||||
|
||||
template handleJmpBack() {.dirty.} =
|
||||
@@ -663,7 +663,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
of rkNode:
|
||||
if regs[rb].node.typ.kind notin PtrLikeKinds:
|
||||
stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].node.typ: " & $regs[rb].node.typ.kind)
|
||||
node2.intVal = regs[rb].node.intVal
|
||||
if regs[rb].node.kind == nkNilLit:
|
||||
node2.intVal = 0
|
||||
else:
|
||||
node2.intVal = regs[rb].node.intVal
|
||||
else: stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].kind: " & $regs[rb].kind)
|
||||
regs[ra].node = node2
|
||||
of opcAsgnComplex:
|
||||
@@ -1435,15 +1438,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
else:
|
||||
globalError(c.config, c.debug[pc], "VM not built with FFI support")
|
||||
elif prc.kind != skTemplate:
|
||||
let newPc = compile(c, prc)
|
||||
let procInfo = compile(c, prc)
|
||||
# tricky: a recursion is also a jump back, so we use the same
|
||||
# logic as for loops:
|
||||
if newPc < pc: handleJmpBack()
|
||||
if procInfo.pc < pc: handleJmpBack()
|
||||
#echo "new pc ", newPc, " calling: ", prc.name.s
|
||||
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
|
||||
newSeq(newFrame.slots, prc.offset+ord(isClosure))
|
||||
if not isEmptyType(prc.typ.returnType):
|
||||
putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config))
|
||||
newSeq(newFrame.slots, procInfo.usedRegisters+ord(isClosure))
|
||||
# setup slot for proc result:
|
||||
let ret {.cursor.} = prc.typ.returnType
|
||||
# hot spot ahead!
|
||||
if ret != nil:
|
||||
if fitsRegister(ret):
|
||||
# same logic as opcLdNullReg here:
|
||||
ensureKind(newFrame.slots[0], rkInt)
|
||||
newFrame.slots[0].intVal = 0
|
||||
elif not isEmptyType(ret):
|
||||
putIntoReg(newFrame.slots[0], getNullValue(c, ret, prc.info, c.config))
|
||||
for i in 1..rc-1:
|
||||
newFrame.slots[i] = regs[rb+i]
|
||||
if isClosure:
|
||||
@@ -1459,7 +1470,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
tos = newFrame
|
||||
updateRegsAlias
|
||||
# -1 for the following 'inc pc'
|
||||
pc = newPc-1
|
||||
pc = procInfo.pc-1
|
||||
else:
|
||||
# for 'getAst' support we need to support template expansion here:
|
||||
let genSymOwner = if tos.next != nil and tos.next.prc != nil:
|
||||
@@ -1715,6 +1726,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let max = (1.BiggestInt shl (rb-1))-1
|
||||
if regs[ra].intVal < min or regs[ra].intVal > max:
|
||||
stackTrace(c, tos, pc, "unhandled exception: value out of range")
|
||||
of opcNarrowR:
|
||||
decodeBC(rkInt)
|
||||
let min = regs[rb].intVal
|
||||
let max = regs[rc].intVal
|
||||
if regs[ra].intVal < min or regs[ra].intVal > max:
|
||||
stackTrace(c, tos, pc, "unhandled exception: value out of range")
|
||||
of opcNarrowU:
|
||||
decodeB(rkInt)
|
||||
regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1)
|
||||
@@ -2343,8 +2360,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
let start = genProc(c, sym)
|
||||
|
||||
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
|
||||
let maxSlots = sym.offset
|
||||
newSeq(tos.slots, maxSlots)
|
||||
newSeq(tos.slots, start.usedRegisters)
|
||||
|
||||
# setup parameters:
|
||||
if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
|
||||
@@ -2353,7 +2369,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
for i in 0..<sym.typ.paramsLen:
|
||||
putIntoReg(tos.slots[i+1], args[i])
|
||||
|
||||
result = rawExecute(c, start, tos).regToNode
|
||||
result = rawExecute(c, start.pc, tos).regToNode
|
||||
else:
|
||||
result = nil
|
||||
localError(c.config, sym.info,
|
||||
@@ -2542,8 +2558,7 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
|
||||
return errorNode(idgen, module, n)
|
||||
|
||||
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
|
||||
let maxSlots = sym.offset
|
||||
newSeq(tos.slots, maxSlots)
|
||||
newSeq(tos.slots, start.usedRegisters)
|
||||
# setup arguments:
|
||||
var L = n.safeLen
|
||||
if L == 0: L = 1
|
||||
@@ -2570,7 +2585,7 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
|
||||
" generic parameter(s)")
|
||||
# temporary storage:
|
||||
#for i in L..<maxSlots: tos.slots[i] = newNode(nkEmpty)
|
||||
result = rawExecute(c, start, tos).regToNode
|
||||
result = rawExecute(c, start.pc, tos).regToNode
|
||||
if result.info.line < 0: result.info = n.info
|
||||
if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree")
|
||||
dec(g.config.evalMacroCounter)
|
||||
|
||||
@@ -105,7 +105,7 @@ type
|
||||
opcIsNil, opcOf, opcIs,
|
||||
opcParseFloat, opcConv, opcCast,
|
||||
opcQuit, opcInvalidField,
|
||||
opcNarrowS, opcNarrowU,
|
||||
opcNarrowS, opcNarrowU, opcNarrowR
|
||||
opcSignExtend,
|
||||
|
||||
opcAddStrCh,
|
||||
@@ -243,6 +243,11 @@ type
|
||||
VmCallback* = proc (args: VmArgs) {.closure.}
|
||||
|
||||
PCtx* = ref TCtx
|
||||
|
||||
VmProcInfo* = object
|
||||
pc*: int32
|
||||
usedRegisters*: int32
|
||||
|
||||
TCtx* = object of TPassContext # code gen context
|
||||
code*: seq[TInstr]
|
||||
debug*: seq[TLineInfo] # line info for every instruction; kept separate
|
||||
@@ -271,7 +276,7 @@ type
|
||||
profiler*: Profiler
|
||||
templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst
|
||||
vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC)
|
||||
procToCodePos*: Table[int, int]
|
||||
procToCodePos*: Table[int, VmProcInfo]
|
||||
cannotEval*: bool
|
||||
locals*: IntSet
|
||||
|
||||
@@ -293,6 +298,9 @@ type
|
||||
|
||||
PEvalContext* = PCtx
|
||||
|
||||
const
|
||||
NoVmProcInfo* = VmProcInfo(pc: 0'i32, usedRegisters: -1'i32)
|
||||
|
||||
proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator): PCtx =
|
||||
PCtx(code: @[], debug: @[],
|
||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||
|
||||
@@ -798,6 +798,13 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8):
|
||||
c.gABC(n, opcNarrowS, dest, TRegister(size*8))
|
||||
elif t.kind in {tyEnum, tyRange}:
|
||||
let intType = getSysType(c.graph, n.info, tyInt)
|
||||
let first = c.genx(newIntTypeNode(firstOrd(c.config, t), intType))
|
||||
let last = c.genx(newIntTypeNode(lastOrd(c.config, t), intType))
|
||||
c.gABC(n, opcNarrowR, dest, first, last)
|
||||
c.freeTemp(first)
|
||||
c.freeTemp(last)
|
||||
|
||||
proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
|
||||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
@@ -1069,6 +1076,19 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
|
||||
else:
|
||||
(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
|
||||
|
||||
proc sizeLog2(typeSize: BiggestInt): TRegister =
|
||||
case typeSize:
|
||||
of 8:
|
||||
result = 3
|
||||
of 16:
|
||||
result = 4
|
||||
of 32:
|
||||
result = 5
|
||||
of 64:
|
||||
result = 6
|
||||
else:
|
||||
raiseAssert $(typeSize)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||
@@ -1154,24 +1174,42 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMag
|
||||
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
|
||||
of mShrI:
|
||||
# modified: genBinaryABC(c, n, dest, opcShrInt)
|
||||
# narrowU is applied to the left operandthe idea here is to narrow the left operand
|
||||
# narrowU is applied to the left operand the idea here is to narrow the left operand
|
||||
let typ = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, typ)
|
||||
let tmp = c.genx(n[1])
|
||||
c.genNarrowU(n, tmp)
|
||||
let tmp2 = c.genx(n[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNarrowU, tmp2, sizeLog2(size * 8))
|
||||
c.gABC(n, opcShrInt, dest, tmp, tmp2)
|
||||
c.freeTemp(tmp)
|
||||
c.freeTemp(tmp2)
|
||||
of mShlI:
|
||||
genBinaryABC(c, n, dest, opcShlInt)
|
||||
let typ = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, typ)
|
||||
let tmp1 = c.genx(n[1])
|
||||
let tmp2 = c.genx(n[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNarrowU, tmp2, sizeLog2(size * 8))
|
||||
c.gABC(n, opcShlInt, dest, tmp1, tmp2)
|
||||
c.freeTemp(tmp1)
|
||||
c.freeTemp(tmp2)
|
||||
# genNarrowU modified
|
||||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, t)
|
||||
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
|
||||
if typ.kind in {tyUInt8..tyUInt32} or (typ.kind == tyUInt and size < 8):
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8):
|
||||
elif typ.kind in {tyInt8..tyInt32} or (typ.kind == tyInt and size < 8):
|
||||
c.gABC(n, opcSignExtend, dest, TRegister(size*8))
|
||||
of mAshrI: genBinaryABC(c, n, dest, opcAshrInt)
|
||||
of mAshrI:
|
||||
let typ = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, typ)
|
||||
let tmp1 = c.genx(n[1])
|
||||
let tmp2 = c.genx(n[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNarrowU, tmp2, sizeLog2(size * 8))
|
||||
c.gABC(n, opcAshrInt, dest, tmp1, tmp2)
|
||||
c.freeTemp(tmp1)
|
||||
c.freeTemp(tmp2)
|
||||
of mBitandI: genBinaryABC(c, n, dest, opcBitandInt)
|
||||
of mBitorI: genBinaryABC(c, n, dest, opcBitorInt)
|
||||
of mBitxorI: genBinaryABC(c, n, dest, opcBitxorInt)
|
||||
@@ -1230,7 +1268,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMag
|
||||
var tmp = c.genx(n[1])
|
||||
c.gABC(n, opcQuit, tmp)
|
||||
c.freeTemp(tmp)
|
||||
of mSetLengthStr, mSetLengthSeq:
|
||||
of mSetLengthStr, mSetLengthSeq, mSetLengthSeqUninit:
|
||||
unused(c, n, dest)
|
||||
var d = c.genx(n[1])
|
||||
var tmp = c.genx(n[2])
|
||||
@@ -2128,7 +2166,7 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
c.preventFalseAlias(it, opcWrObj, dest, i.TRegister, tmp)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genProc*(c: PCtx; s: PSym): int
|
||||
proc genProc*(c: PCtx; s: PSym): VmProcInfo
|
||||
|
||||
proc toKey(s: PSym): string =
|
||||
result = ""
|
||||
@@ -2412,27 +2450,19 @@ proc optimizeJumps(c: PCtx; start: int) =
|
||||
c.finalJumpTarget(i, d - i)
|
||||
else: discard
|
||||
|
||||
proc genProc(c: PCtx; s: PSym): int =
|
||||
let
|
||||
pos = c.procToCodePos.getOrDefault(s.id)
|
||||
wasNotGenProcBefore = pos == 0
|
||||
noRegistersAllocated = s.offset == -1
|
||||
if wasNotGenProcBefore or noRegistersAllocated:
|
||||
# xxx: the noRegisterAllocated check is required in order to avoid issues
|
||||
# where nimsuggest can crash due as a macro with pos will be loaded
|
||||
# but it doesn't have offsets for register allocations see:
|
||||
# https://github.com/nim-lang/Nim/issues/18385
|
||||
# Improvements and further use of IC should remove the need for this.
|
||||
proc genProc(c: PCtx; s: PSym): VmProcInfo =
|
||||
result = c.procToCodePos.getOrDefault(s.id, NoVmProcInfo)
|
||||
if result.usedRegisters < 0:
|
||||
#if s.name.s == "outterMacro" or s.name.s == "innerProc":
|
||||
# echo "GENERATING CODE FOR ", s.name.s
|
||||
let last = c.code.len-1
|
||||
var eofInstr: TInstr = default(TInstr)
|
||||
var eofInstr = default(TInstr)
|
||||
if last >= 0 and c.code[last].opcode == opcEof:
|
||||
eofInstr = c.code[last]
|
||||
c.code.setLen(last)
|
||||
c.debug.setLen(last)
|
||||
#c.removeLastEof
|
||||
result = c.code.len+1 # skip the jump instruction
|
||||
result.pc = (c.code.len+1).int32 # skip the jump instruction
|
||||
c.procToCodePos[s.id] = result
|
||||
# thanks to the jmp we can add top level statements easily and also nest
|
||||
# procs easily:
|
||||
@@ -2457,12 +2487,12 @@ proc genProc(c: PCtx; s: PSym): int =
|
||||
c.gABC(body, opcRet)
|
||||
c.patch(procStart)
|
||||
c.gABC(body, opcEof, eofInstr.regA)
|
||||
c.optimizeJumps(result)
|
||||
s.offset = c.prc.regInfo.len.int32
|
||||
c.optimizeJumps(result.pc)
|
||||
result.usedRegisters = c.prc.regInfo.len.int32
|
||||
c.procToCodePos[s.id] = result
|
||||
#if s.name.s == "main" or s.name.s == "[]":
|
||||
# echo renderTree(body)
|
||||
# c.echoCode(result)
|
||||
c.prc = oldPrc
|
||||
else:
|
||||
c.prc.regInfo.setLen s.offset
|
||||
result = pos
|
||||
c.prc.regInfo.setLen result.usedRegisters
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
nim_comment="key-value pairs for windows/posix bootstrapping build scripts"
|
||||
nim_csourcesDir=csources_v2
|
||||
nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git
|
||||
nim_csourcesDir=csources_v3
|
||||
nim_csourcesUrl=https://github.com/nim-lang/csources_v3.git
|
||||
nim_csourcesBranch=master
|
||||
nim_csourcesHash=86742fb02c6606ab01a532a0085784effb2e753e
|
||||
nim_csourcesHash=eeab3ac46e93f10efda8e58c4db02b9438319d71
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
=====================================================
|
||||
Nim -- a Compiler for Nim. https://nim-lang.org/
|
||||
|
||||
Copyright (C) 2006-2025 Andreas Rumpf. All rights reserved.
|
||||
Copyright (C) 2006-2026 Andreas Rumpf. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -90,6 +90,7 @@ Advanced options:
|
||||
--os:SYMBOL set the target operating system (cross-compilation)
|
||||
--cpu:SYMBOL set the target processor (cross-compilation)
|
||||
--debuginfo:on|off enables debug information
|
||||
--mangle:nim|cpp selects `nim` or `cpp` style name mangling, defaults to `nim`
|
||||
-t, --passC:OPTION pass an option to the C compiler
|
||||
-l, --passL:OPTION pass an option to the linker
|
||||
--cc:SYMBOL specify the C compiler
|
||||
@@ -113,6 +114,7 @@ Advanced options:
|
||||
--docSeeSrcUrl:url activate 'see source' for doc command
|
||||
(see doc.item.seesrc in config/nimdoc.cfg)
|
||||
--docInternal also generate documentation for non-exported symbols
|
||||
--raw turn off markup rendering for JSON docs
|
||||
--lineDir:on|off generation of #line directive on|off
|
||||
--embedsrc:on|off embeds the original source code as comments
|
||||
in the generated output
|
||||
|
||||
@@ -3008,6 +3008,12 @@ is more specific
|
||||
2. if the concept is being compared with another concept the result is deferred to [Concept subset matching]
|
||||
3. in any other case the concept is less specific then it's competitor
|
||||
|
||||
Currently, the concept evaluation mechanism evaluates to a successful match on the first acceptable candidate
|
||||
for each defined binding. This has a couple of notable effects:
|
||||
|
||||
- generic parameters are fulfilled by the first candidate match even if other candidates would also match and bind different parameters
|
||||
- inheritable objects match as they do in normal overload resolution except the "depth" is not accounted for, because that would require calculating the minimum depth of any matching binding
|
||||
|
||||
|
||||
Concept subset matching
|
||||
-------------------------
|
||||
@@ -3019,6 +3025,44 @@ If neither of them are subsets of one another, then the disambiguation proceeds
|
||||
and the concept with the most definitions wins, if any. No definite winner is an ambiguity error at
|
||||
compile time.
|
||||
|
||||
Recursive concepts
|
||||
------------------
|
||||
|
||||
Concepts can reference themselves in their definitions, enabling recursive type constraints.
|
||||
This is useful for matching `distinct` types that should inherit traits from their base type:
|
||||
|
||||
```nim
|
||||
import std/typetraits
|
||||
|
||||
type
|
||||
PrimitiveBase = SomeNumber | bool | ptr | pointer | enum
|
||||
|
||||
# Matches PrimitiveBase directly, or any distinct type whose base is Primitive
|
||||
Primitive = concept x
|
||||
x is PrimitiveBase or distinctBase(x) is Primitive
|
||||
|
||||
# Application: a handle type that should be treated like a primitive
|
||||
Handle = distinct int
|
||||
SpecialHandle = distinct Handle
|
||||
|
||||
assert int is Primitive
|
||||
assert Handle is Primitive
|
||||
assert SpecialHandle is Primitive # works through 2 levels
|
||||
assert not (string is Primitive)
|
||||
```
|
||||
|
||||
Concepts can also be mutually recursive (co-dependent):
|
||||
|
||||
```nim
|
||||
type
|
||||
Serializable = concept
|
||||
proc serialize(s: Self; writer: var Writer)
|
||||
Writer = concept
|
||||
proc write(w: var Self; data: Serializable)
|
||||
```
|
||||
|
||||
The compiler uses cycle detection to handle these cases without infinite recursion.
|
||||
|
||||
Statements and expressions
|
||||
==========================
|
||||
|
||||
@@ -7937,6 +7981,35 @@ underlying C `struct`:c: in a `sizeof` expression:
|
||||
```
|
||||
|
||||
|
||||
CompleteStruct pragma
|
||||
---------------------
|
||||
The `completeStruct` pragma is a contract indicating that an `importc` type
|
||||
declaration contains all fields of the corresponding C type, allowing
|
||||
`sizeof`, `alignof`, and `offsetof` to be computed at compile-time.
|
||||
|
||||
By default, `importc` types are assumed to be incomplete (their size is
|
||||
unknown at compile-time). Use `completeStruct` when you need compile-time
|
||||
size information and can guarantee the Nim definition matches the C layout:
|
||||
|
||||
```Nim
|
||||
type
|
||||
InotifyEvent {.importc: "struct inotify_event", header: "<sys/inotify.h>",
|
||||
completeStruct.} = object
|
||||
wd: cint
|
||||
mask: uint32
|
||||
cookie: uint32
|
||||
len: uint32
|
||||
# All fields must match the C struct exactly
|
||||
```
|
||||
|
||||
If the Nim fields don't match the C struct, a static assertion will fail
|
||||
during C code generation.
|
||||
|
||||
Without `completeStruct`, attempting to use `sizeof` on an `importc` type
|
||||
at compile-time will error with "'sizeof' requires '.importc' types to be
|
||||
'.completeStruct'".
|
||||
|
||||
|
||||
Compile pragma
|
||||
--------------
|
||||
The `compile` pragma can be used to compile and link a C/C++ source file
|
||||
|
||||
@@ -580,6 +580,8 @@ Define Effect
|
||||
Currently only clang and vcc.
|
||||
`strip` Strip debug symbols added by the backend compiler from
|
||||
the executable.
|
||||
`heaptrack` Track memory allocations using
|
||||
[heaptrack](https://github.com/KDE/heaptrack)
|
||||
====================== =========================================================
|
||||
|
||||
|
||||
|
||||
@@ -181,6 +181,7 @@ body {
|
||||
|
||||
.nine.columns {
|
||||
width: 75.0%;
|
||||
margin-left: 0;
|
||||
padding-left: 1.5em; }
|
||||
|
||||
.twelve.columns {
|
||||
@@ -192,7 +193,9 @@ body {
|
||||
display: none;
|
||||
}
|
||||
.nine.columns {
|
||||
width: 98.0%;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
body {
|
||||
font-size: 1em;
|
||||
|
||||
@@ -1278,9 +1278,10 @@ Arrays can be constructed using `[]`:
|
||||
echo x[i]
|
||||
```
|
||||
|
||||
The notation `x[i]` is used to access the i-th element of `x`.
|
||||
Array access is always bounds checked (at compile-time or at runtime). These
|
||||
checks can be disabled via pragmas or invoking the compiler with the
|
||||
The notation `x[i]` is used to access the i-th element of `x` in the example
|
||||
above. Valid indexes can be defined by any subrange. Array access is
|
||||
always bounds checked (at compile-time or at runtime). These checks can be
|
||||
disabled via pragmas or invoking the compiler with the
|
||||
``--bound_checks:off`` command line switch.
|
||||
|
||||
Arrays are value types, like any other Nim type. The assignment operator
|
||||
|
||||
4
koch.nim
4
koch.nim
@@ -12,9 +12,9 @@
|
||||
const
|
||||
# examples of possible values for repos: Head, ea82b54
|
||||
NimbleStableCommit = "9207e8b2bbdf66b5a4d1020214cff44d2d30df92" # 0.20.1
|
||||
AtlasStableCommit = "2aa62121b40d580aa2fb27920a37b938d36c5f57" # 0.9.4
|
||||
AtlasStableCommit = "ff1f4289482dce94ba9f95b3b0ae16d16e21eb3d" # 0.10.1
|
||||
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
|
||||
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
|
||||
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
|
||||
|
||||
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
|
||||
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
|
||||
|
||||
@@ -433,15 +433,15 @@ when defined(nimHasNoReturnError):
|
||||
else:
|
||||
{.pragma: errorNoReturn.}
|
||||
|
||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign, errorNoReturn.}
|
||||
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", gcsafe, errorNoReturn.}
|
||||
## Writes an error message at compile time. The optional `n: NimNode`
|
||||
## parameter is used as the source for file and line number information in
|
||||
## the compilation error message.
|
||||
|
||||
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
|
||||
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", gcsafe.}
|
||||
## Writes a warning message at compile time.
|
||||
|
||||
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
|
||||
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", gcsafe.}
|
||||
## Writes a hint message at compile time.
|
||||
|
||||
proc newStrLitNode*(s: string): NimNode {.noSideEffect.} =
|
||||
@@ -511,7 +511,7 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
|
||||
## Generates a fresh symbol that is guaranteed to be unique. The symbol
|
||||
## needs to occur in a declaration context.
|
||||
|
||||
proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated:
|
||||
proc callsite*(): NimNode {.magic: "NCallSite", gcsafe, deprecated:
|
||||
"Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead".}
|
||||
## Returns the AST of the invocation expression that invoked this macro.
|
||||
# see https://github.com/nim-lang/RFCs/issues/387 as candidate replacement.
|
||||
@@ -933,7 +933,7 @@ proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
|
||||
|
||||
const collapseSymChoice = not defined(nimLegacyMacrosCollapseSymChoice)
|
||||
|
||||
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
|
||||
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.gcsafe.} =
|
||||
if level > 0:
|
||||
if indented:
|
||||
res.add("\n")
|
||||
@@ -982,21 +982,21 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent
|
||||
if isLisp:
|
||||
res.add(")")
|
||||
|
||||
proc treeRepr*(n: NimNode): string {.benign.} =
|
||||
proc treeRepr*(n: NimNode): string {.gcsafe.} =
|
||||
## Convert the AST `n` to a human-readable tree-like string.
|
||||
##
|
||||
## See also `repr`, `lispRepr`_, and `astGenRepr`_.
|
||||
result = ""
|
||||
n.treeTraverse(result, isLisp = false, indented = true)
|
||||
|
||||
proc lispRepr*(n: NimNode; indented = false): string {.benign.} =
|
||||
proc lispRepr*(n: NimNode; indented = false): string {.gcsafe.} =
|
||||
## Convert the AST `n` to a human-readable lisp-like string.
|
||||
##
|
||||
## See also `repr`, `treeRepr`_, and `astGenRepr`_.
|
||||
result = ""
|
||||
n.treeTraverse(result, isLisp = true, indented = indented)
|
||||
|
||||
proc astGenRepr*(n: NimNode): string {.benign.} =
|
||||
proc astGenRepr*(n: NimNode): string {.gcsafe.} =
|
||||
## Convert the AST `n` to the code required to generate that AST.
|
||||
##
|
||||
## See also `repr`_, `treeRepr`_, and `lispRepr`_.
|
||||
@@ -1005,7 +1005,7 @@ proc astGenRepr*(n: NimNode): string {.benign.} =
|
||||
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
|
||||
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
|
||||
|
||||
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
|
||||
proc traverse(res: var string, level: int, n: NimNode) {.gcsafe.} =
|
||||
for i in 0..level-1: res.add " "
|
||||
if n.kind in NodeKinds:
|
||||
res.add("new" & ($n.kind).substr(3) & "Node(")
|
||||
|
||||
@@ -118,21 +118,21 @@ when not defined(js):
|
||||
template `rawType=`(x: var Any, p: PNimType) =
|
||||
x.rawTypePtr = cast[pointer](p)
|
||||
|
||||
proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
|
||||
proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc, raises: [].}
|
||||
|
||||
when not defined(gcDestructors):
|
||||
proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
|
||||
proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.}
|
||||
proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.}
|
||||
proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.}
|
||||
proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc, raises: [].}
|
||||
proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc, raises: [].}
|
||||
proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc, raises: [].}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc, raises: [].}
|
||||
proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc, raises: [].}
|
||||
else:
|
||||
proc nimNewObj(size, align: int): pointer {.importCompilerProc.}
|
||||
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.}
|
||||
proc nimNewObj(size, align: int): pointer {.importCompilerProc, raises: [].}
|
||||
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc, raises: [].}
|
||||
proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
|
||||
importCompilerProc.}
|
||||
importCompilerProc, raises: [].}
|
||||
proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {.
|
||||
importCompilerProc.}
|
||||
importCompilerProc, raises: [].}
|
||||
|
||||
include system/ptrarith
|
||||
|
||||
|
||||
@@ -304,6 +304,35 @@ else:
|
||||
proc rotl32(x: uint32, r: int): uint32 {.inline.} =
|
||||
(x shl r) or (x shr (32 - r))
|
||||
|
||||
proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
|
||||
uint32(s[o + 1]) shl 8 or uint32(s[o + 0])
|
||||
|
||||
proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
|
||||
uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
|
||||
uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
|
||||
uint64(s[o + 1]) shl 8 or uint64(s[o + 0])
|
||||
|
||||
when declared(copyMem):
|
||||
from std/endians import littleEndian64, littleEndian32
|
||||
|
||||
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
when nimvm: result = load4e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint32(0)
|
||||
littleEndian32(addr result, addr s[o])
|
||||
else: result = load4e(s, o)
|
||||
|
||||
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
when nimvm: result = load8e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint64(0)
|
||||
littleEndian64(addr result, addr s[o])
|
||||
else: result = load8e(s, o)
|
||||
|
||||
proc murmurHash(x: openArray[byte]): Hash =
|
||||
# https://github.com/PeterScott/murmur3/blob/master/murmur3.c
|
||||
const
|
||||
@@ -320,24 +349,10 @@ proc murmurHash(x: openArray[byte]): Hash =
|
||||
h1: uint32 = uint32(0)
|
||||
i = 0
|
||||
|
||||
|
||||
template impl =
|
||||
var j = stepSize
|
||||
while j > 0:
|
||||
dec j
|
||||
k1 = (k1 shl 8) or (ord(x[i+j])).uint32
|
||||
|
||||
# body
|
||||
while i < n * stepSize:
|
||||
var k1: uint32 = uint32(0)
|
||||
var k1 = load4(x, i)
|
||||
|
||||
when nimvm:
|
||||
impl()
|
||||
else:
|
||||
when declared(copyMem):
|
||||
copyMem(addr k1, addr x[i], 4)
|
||||
else:
|
||||
impl()
|
||||
inc i, stepSize
|
||||
|
||||
k1 = imul(k1, c1)
|
||||
@@ -384,32 +399,6 @@ const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses
|
||||
const k1 = 0xb492b66fbe98f273u64
|
||||
const k2 = 0x9ae16a3b2f90404fu64
|
||||
|
||||
proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
|
||||
uint32(s[o + 1]) shl 8 or uint32(s[o + 0])
|
||||
|
||||
proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
|
||||
uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
|
||||
uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
|
||||
uint64(s[o + 1]) shl 8 or uint64(s[o + 0])
|
||||
|
||||
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
when nimvm: result = load4e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint32(0)
|
||||
copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load4e(s, o)
|
||||
|
||||
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
when nimvm: result = load8e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint64(0)
|
||||
copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load8e(s, o)
|
||||
|
||||
proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
|
||||
|
||||
proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47)
|
||||
|
||||
@@ -573,7 +573,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeade
|
||||
result = $httpMethod
|
||||
result.add ' '
|
||||
|
||||
if proxy.isNil or (requestUrl.scheme == "https" and proxy.url.scheme == "socks5h"):
|
||||
if proxy.isNil or requestUrl.scheme == "https":
|
||||
# /path?query
|
||||
if not requestUrl.path.startsWith("/"): result.add '/'
|
||||
result.add(requestUrl.path)
|
||||
|
||||
@@ -57,8 +57,12 @@ proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if (e == EINVAL or e == EOPNOTSUPP) and ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError() # fallback arguable; Most portable BUT allows SEGV
|
||||
if e == EINVAL or e == EOPNOTSUPP or e == ENOSYS:
|
||||
# fallback arguable; Most portable BUT allows SEGV
|
||||
if ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
else:
|
||||
discard
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
else: # shrink the file
|
||||
|
||||
@@ -170,7 +170,7 @@ runnableExamples:
|
||||
assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
|
||||
assert dict.getSectionValue(section4, "purpose") == "formatting for readability"
|
||||
|
||||
import std/[strutils, lexbase, streams, tables]
|
||||
import std/[strformat, strutils, lexbase, streams, tables]
|
||||
import std/private/decode_helpers
|
||||
import std/private/since
|
||||
|
||||
@@ -220,7 +220,7 @@ type
|
||||
const
|
||||
SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', ' ', '\x80'..'\xFF', '.', '/', '\\', '-'}
|
||||
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe, raises: [ValueError, OSError, IOError].}
|
||||
|
||||
proc open*(c: var CfgParser, input: Stream, filename: string,
|
||||
lineOffset = 0) {.rtl, extern: "npc$1".} =
|
||||
@@ -428,14 +428,12 @@ proc rawGetTok(c: var CfgParser, tok: var Token) =
|
||||
proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## Returns a properly formatted error message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Error: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
&"{c.filename}({getLine(c)}, {getColumn(c)}) Error: {msg}"
|
||||
|
||||
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## Returns a properly formatted warning message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Warning: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
&"{c.filename}({getLine(c)}, {getColumn(c)}) Warning: {msg}"
|
||||
|
||||
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
|
||||
## Returns a properly formatted warning message containing that
|
||||
|
||||
@@ -14,149 +14,306 @@
|
||||
## Supported Syntax
|
||||
## ================
|
||||
##
|
||||
## The following syntax is supported when arguments for the `shortNoVal` and
|
||||
## `longNoVal` parameters, which are
|
||||
## `described later<#nimshortnoval-and-nimlongnoval>`_, are not provided:
|
||||
## The syntax described here applies to the default way the parser works.
|
||||
## The behavior is configurable, though, and two additional modes
|
||||
## are supported, see the details: `Parser Modes`_.
|
||||
##
|
||||
## 1. Short options: `-abcd`, `-e:5`, `-e=5`
|
||||
## Parsing also depends on whether the `shortNoVal` and `longNoVal` parameters
|
||||
## are omitted/empty or provided. The details are described in a
|
||||
## `later section<#nimshortnoval-and-nimlongnoval>`_.
|
||||
##
|
||||
## The following syntax is supported:
|
||||
##
|
||||
## 1. Short options: `-a:5`, `-b=5`, `-cde`, `-fgh=5`
|
||||
## 2. Long options: `--foo:bar`, `--foo=bar`, `--foo`
|
||||
## 3. Arguments: everything that does not start with a `-`
|
||||
##
|
||||
## These three kinds of tokens are enumerated in the
|
||||
## `CmdLineKind enum<#CmdLineKind>`_.
|
||||
## Passing values to options **requires** a separator (`:`/`=`), short options
|
||||
## (flags) can be bundled together and the last one can take a value.
|
||||
##
|
||||
## When option values begin with ':' or '=', they need to be doubled up (as in
|
||||
## `--delim::`) or alternated (as in `--delim=:`).
|
||||
## Option values can begin with the separator character (`:`/`=`), so all of the
|
||||
## following is valid:
|
||||
## - option `foo`, value `:`: `--foo::`, `--foo=:`
|
||||
## - option `foo`, value `=`: `--foo:=`, `--foo==`
|
||||
##
|
||||
## The `--` option, commonly used to denote that every token that follows is
|
||||
## an argument, is interpreted as a long option, and its name is the empty
|
||||
## string.
|
||||
## string. Trailing arguments can be accessed with `remainingArgs<#remainingArgs,OptParser>`_
|
||||
## or `cmdLineRest<#cmdLineRest,OptParser>`_.
|
||||
##
|
||||
## Parsing
|
||||
## =======
|
||||
##
|
||||
## Use an `OptParser<#OptParser>`_ to parse command line options. It can be
|
||||
## created with `initOptParser<#initOptParser,string,set[char],seq[string]>`_,
|
||||
## and `next<#next,OptParser>`_ advances the parser by one token.
|
||||
## To parse command line options, use the `getopt iterator<#getopt.i,OptParser>`_.
|
||||
## It initializes the `OptParser<#OptParser>`_ object internally and iterates
|
||||
## through the command line options.
|
||||
##
|
||||
## For each token, the parser's `kind`, `key`, and `val` fields give
|
||||
## information about that token. If the token is a long or short option, `key`
|
||||
## is the option's name, and `val` is either the option's value, if provided,
|
||||
## or the empty string. For arguments, the `key` field contains the argument
|
||||
## itself, and `val` is unused. To check if the end of the command line has
|
||||
## been reached, check if `kind` is equal to `cmdEnd`.
|
||||
## For each token, the parser's `kind` (`CmdLineKind enum<#CmdLineKind>`_.),
|
||||
## `key`, and `val` fields are yielded.
|
||||
##
|
||||
## For long and short options, `key` is the option's name, and `val` is either
|
||||
## the option's value, if given, or an empty string. For arguments, the `key`
|
||||
## field contains the argument itself, and `val` is unused (empty).
|
||||
##
|
||||
## Here is an example:
|
||||
##
|
||||
## ```Nim
|
||||
## import std/parseopt
|
||||
runnableExamples:
|
||||
import std/os
|
||||
|
||||
let cmds = "-ab -e:5 --foo --bar=20 file.txt".parseCmdLine()
|
||||
var output: seq[string] = @[]
|
||||
# If cmds is not supplied, real arguments will be retrieved by the `os` module
|
||||
for kind, key, val in getopt(cmds):
|
||||
case kind
|
||||
of cmdEnd: break
|
||||
of cmdShortOption, cmdLongOption:
|
||||
if val == "":
|
||||
output.add("Option: " & key)
|
||||
else:
|
||||
output.add("Option and value: " & key & ", " & val)
|
||||
of cmdArgument:
|
||||
output.add("Argument: " & key)
|
||||
|
||||
doAssert output == @[
|
||||
"Option: a",
|
||||
"Option: b",
|
||||
"Option and value: e, 5",
|
||||
"Option: foo",
|
||||
"Option and value: bar, 20",
|
||||
"Argument: file.txt"
|
||||
]
|
||||
##
|
||||
## var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt")
|
||||
## while true:
|
||||
## p.next()
|
||||
## case p.kind
|
||||
## of cmdEnd: break
|
||||
## of cmdShortOption, cmdLongOption:
|
||||
## if p.val == "":
|
||||
## echo "Option: ", p.key
|
||||
## else:
|
||||
## echo "Option and value: ", p.key, ", ", p.val
|
||||
## of cmdArgument:
|
||||
## echo "Argument: ", p.key
|
||||
## The `OptParser<#OptParser>`_ can be initialized with
|
||||
## `initOptParser<#initOptParser,string,set[char],seq[string]>`_.
|
||||
## The `next<#next,OptParser>`_ proc advances the parser by one token.
|
||||
##
|
||||
## # Output:
|
||||
## # Option: a
|
||||
## # Option: b
|
||||
## # Option and value: e, 5
|
||||
## # Option: foo
|
||||
## # Option and value: bar, 20
|
||||
## # Argument: file.txt
|
||||
## ```
|
||||
## When iterating the object manually with `next<#next,OptParser>`_, reaching
|
||||
## the end of the command line is signalled by setting the `kind` field
|
||||
## to `cmdEnd`.
|
||||
##
|
||||
## The `getopt iterator<#getopt.i,OptParser>`_, which is provided for
|
||||
## convenience, can be used to iterate through all command line options as well.
|
||||
## To set a default value for an option, assign the default value to a variable
|
||||
## beforehand, then update it while parsing.
|
||||
##
|
||||
## To set a default value for a variable assigned through `getopt` and accept arguments from the cmd line.
|
||||
## Assign the default value to a variable before parsing.
|
||||
## Then set the variable to the new value while parsing.
|
||||
##
|
||||
## Here is an example:
|
||||
##
|
||||
## ```Nim
|
||||
## import std/parseopt
|
||||
##
|
||||
## var varName: string = "defaultValue"
|
||||
##
|
||||
## for kind, key, val in getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## discard
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key:
|
||||
## of "varName": # --varName:<value> in the console when executing
|
||||
## varName = val # do input sanitization in production systems
|
||||
## of cmdEnd:
|
||||
## discard
|
||||
## ```
|
||||
runnableExamples:
|
||||
import std/strutils
|
||||
|
||||
var varName: string = "defaultValue"
|
||||
|
||||
for kind, key, val in getopt(@["--varName:HELLO"]):
|
||||
case kind
|
||||
of cmdArgument:
|
||||
discard
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "varName": # --varName:<value> in the console when executing
|
||||
varName = val.toLowerAscii() # do input sanitization in production
|
||||
of cmdEnd:
|
||||
discard
|
||||
|
||||
doAssert varName == "hello"
|
||||
##
|
||||
## `shortNoVal` and `longNoVal`
|
||||
## ============================
|
||||
##
|
||||
## The optional `shortNoVal` and `longNoVal` parameters present in
|
||||
## `initOptParser<#initOptParser,string,set[char],seq[string]>`_ are for
|
||||
## The optional `shortNoVal` and `longNoVal` parameters in
|
||||
## `initOptParser<#initOptParser,string,set[char],seq[string]>`_ and
|
||||
## `getopt iterator<#getopt.i,OptParser>`_ are for
|
||||
## specifying which short and long options do not accept values.
|
||||
##
|
||||
## When `shortNoVal` is non-empty, users are not required to separate short
|
||||
## options and their values with a ':' or '=' since the parser knows which
|
||||
## options accept values and which ones do not. This behavior also applies for
|
||||
## long options if `longNoVal` is non-empty. For short options, `-j4`
|
||||
## becomes supported syntax, and for long options, `--foo bar` becomes
|
||||
## supported. This is in addition to the `previously mentioned
|
||||
## syntax<#supported-syntax>`_. Users can still separate options and their
|
||||
## values with ':' or '=', but that becomes optional.
|
||||
## When `shortNoVal` or `longNoVal` is non-empty, using the separators (`:`/`=`)
|
||||
## becomes non-mandatory and users can separate a value from long
|
||||
## options (that are not supplied to the corresponding argument) by whitespace
|
||||
## or, in the case of a short option, by writing the value directly adjacent to
|
||||
## the option.
|
||||
##
|
||||
## For short options, `-j4` becomes supported syntax (parsed as option `j` with
|
||||
## value `4` instead of two separate options `j` and `4`). For long options,
|
||||
## `--foo bar` becomes supported syntax in all `modes<Parser Modes>`_.
|
||||
##
|
||||
## In `LaxMode` and `GnuMode`, short options can also take values from the next
|
||||
## argument (`-c val`), but this does **not** work in the default `Nim` mode.
|
||||
##
|
||||
## As more options which do not accept values are added to your program,
|
||||
## remember to amend `shortNoVal` and `longNoVal` accordingly.
|
||||
##
|
||||
## The parser does not validate the input for syntax mistakes, thus, options
|
||||
## can still have values if passed explicitly by the user, even when they are
|
||||
## marked as `shortNoVal`/`longNoVal`.
|
||||
##
|
||||
## This behavior allows associating an option with the mistakenly passed value:
|
||||
##
|
||||
runnableExamples:
|
||||
import std/[sequtils, os]
|
||||
|
||||
let cmds = "-n:9 --foo:bar".parseCmdLine()
|
||||
let parsed = toSeq(cmds.getopt(shortNoVal = {'n'}, longNoVal = @["foo"]))
|
||||
for (kind, key, val) in parsed:
|
||||
case kind
|
||||
of cmdEnd: raise newException(AssertionDefect, "Unreachable")
|
||||
of cmdShortOption, cmdLongOption:
|
||||
if key in ["n", "foo"] and val != "":
|
||||
# Substitute for proper error handling in your code
|
||||
discard "Option " & key & " can't take values!"
|
||||
else: discard
|
||||
of cmdArgument: discard
|
||||
doAssert parsed == @[
|
||||
(cmdShortOption, "n", "9"),
|
||||
(cmdLongOption, "foo", "bar")]
|
||||
##
|
||||
## .. Important::
|
||||
## Next-argument value-taking for short/long options is only enabled when
|
||||
## `shortNoVal`/`longNoVal` are non-empty. If your program has *no* options
|
||||
## that take no value, you still must pass a non-empty placeholder (for example,
|
||||
## `shortNoVal = {'\0'}` and/or `longNoVal = @[""]`) to enable this form.
|
||||
##
|
||||
## The following example illustrates the difference between having an empty
|
||||
## `shortNoVal` and `longNoVal`, which is the default, and providing
|
||||
## arguments for those two parameters:
|
||||
##
|
||||
## ```Nim
|
||||
## import std/parseopt
|
||||
runnableExamples:
|
||||
|
||||
proc format(kind: CmdLineKind; key, val: string): string =
|
||||
case kind
|
||||
of cmdEnd: raise newException(AssertionDefect, "Unreachable")
|
||||
of cmdShortOption, cmdLongOption:
|
||||
if val == "": "Option: " & key
|
||||
else: "Option and value: " & key & ", " & val
|
||||
of cmdArgument: "Argument: " & key
|
||||
|
||||
let cmdLine = "-j4 --first bar"
|
||||
var output1, output2: seq[string] = @[]
|
||||
|
||||
var emptyNoVal = initOptParser(cmdLine)
|
||||
for kind, key, val in emptyNoVal.getopt():
|
||||
output1.add format(kind, key, val)
|
||||
|
||||
doAssert output1 == @[
|
||||
"Option: j",
|
||||
"Option: 4",
|
||||
"Option: first",
|
||||
"Argument: bar"
|
||||
]
|
||||
|
||||
var withNoVal = cmdLine.initOptParser(shortNoVal = {'c'},
|
||||
longNoVal = @["second"])
|
||||
for kind, key, val in withNoVal.getopt():
|
||||
output2.add format(kind, key, val)
|
||||
|
||||
doAssert output2 == @[
|
||||
"Option and value: j, 4",
|
||||
"Option and value: first, bar"
|
||||
]
|
||||
##
|
||||
## proc printToken(kind: CmdLineKind, key: string, val: string) =
|
||||
## case kind
|
||||
## of cmdEnd: doAssert(false) # Doesn't happen with getopt()
|
||||
## of cmdShortOption, cmdLongOption:
|
||||
## if val == "":
|
||||
## echo "Option: ", key
|
||||
## else:
|
||||
## echo "Option and value: ", key, ", ", val
|
||||
## of cmdArgument:
|
||||
## echo "Argument: ", key
|
||||
## Parser Modes
|
||||
## ============
|
||||
##
|
||||
## let cmdLine = "-j4 --first bar"
|
||||
## .. Warning:: Modes other than the default (`Nim`) are **experimental** and may
|
||||
## change in future releases.
|
||||
##
|
||||
## var emptyNoVal = initOptParser(cmdLine)
|
||||
## for kind, key, val in emptyNoVal.getopt():
|
||||
## printToken(kind, key, val)
|
||||
## The parser supports several distinct rule sets that change how options are
|
||||
## interpreted:
|
||||
##
|
||||
## # Output:
|
||||
## # Option: j
|
||||
## # Option: 4
|
||||
## # Option: first
|
||||
## # Argument: bar
|
||||
## 1. **LaxMode**: Most forgiving mode, combines `Nim` with POSIX-like
|
||||
## short option handling. Tries to follow the POSIX_ guidelines where possible.
|
||||
## 2. **NimMode**: Standard Nim parsing rules (default).
|
||||
## 3. **GnuMode**: GNU-inspired parsing (e.g. `=` as the only delimiter).
|
||||
## Puts some additional restrictions, following some of the GNU_ conventions.
|
||||
##
|
||||
## var withNoVal = initOptParser(cmdLine, shortNoVal = {'c'},
|
||||
## longNoVal = @["second"])
|
||||
## for kind, key, val in withNoVal.getopt():
|
||||
## printToken(kind, key, val)
|
||||
## Modes are ordered from most relaxed to strictest. The names were
|
||||
## chosen to set general user expectations and full compliance is neither
|
||||
## achieved nor planned.
|
||||
##
|
||||
## # Output:
|
||||
## # Option and value: j, 4
|
||||
## # Option and value: first, bar
|
||||
## ```
|
||||
## Mode Differences
|
||||
## ----------------
|
||||
##
|
||||
## **NimMode** (default):
|
||||
##
|
||||
## - Short options require adjacent values or explicit delimiters:
|
||||
## `-cval`, `-c:val`, `-c=val`
|
||||
## - Short options follow POSIX-style bundling rules
|
||||
## - Next-argument value taking (`-c val`) is **not** supported by default
|
||||
## - Supports both `:` and `=` as delimiters
|
||||
## - Allows whitespace around delimiters
|
||||
## - Values starting with `-` are interpreted as new options
|
||||
##
|
||||
## **LaxMode**:
|
||||
##
|
||||
## - Essentially the Nim mode with some relaxations for short options:
|
||||
## + Allows short options to take values from the next argument: `-c val`
|
||||
## + Supports bundled short options with trailing value: `-abc val`
|
||||
## - Values starting with `-` can be consumed as option arguments
|
||||
##
|
||||
## **GnuMode**:
|
||||
##
|
||||
## - Only `=` is treated as a delimiter (`:` is not a delimiter)
|
||||
## - No whitespace allowed around `=`
|
||||
## - Short options can take next-argument values (`-c val`), but only whitespace
|
||||
## is allowed as a delimiter, separators parse as part of the value
|
||||
## - Short options follow POSIX-style bundling rules
|
||||
## - Values starting with `-` can be consumed as option arguments
|
||||
## - Known discrepancies compared to GNU getopt:
|
||||
## + No notion of optional/mandatory arguments, colon (`:`) doesn't
|
||||
## indicate them and overall is not a special character.
|
||||
##
|
||||
## Mode-Specific Behavior
|
||||
## ----------------------
|
||||
##
|
||||
## The parser's behavior varies significantly between modes, particularly
|
||||
## around how options consume their values:
|
||||
##
|
||||
## **Short Options**
|
||||
##
|
||||
## Consider `-c val`:
|
||||
##
|
||||
## - In `Nim` mode: `-c` is parsed as an option without a value, and `val` is
|
||||
## parsed as a separate argument, regardless of `shortNoVal` being empty or not.
|
||||
## - In `Lax` and `Gnu` modes:
|
||||
## + When `shortNoVal` is empty, or not empty and `-c` is in it:
|
||||
## Same as `Nim`, parsed as option `-c` followed by argument `val`.
|
||||
## + When `-c` is not in `shortNoVal`:
|
||||
## parsed as option `-c`, `val` is consumed as its value.
|
||||
##
|
||||
## Consider `-c-10`:
|
||||
##
|
||||
## - If `shortNoVal` value is empty, all three modes parse three separate short
|
||||
## options: `c`, `1` and `0`.
|
||||
## - Otherwise, if `-c` is not in `shortNoVal`:
|
||||
## + `Nim`: `-c` is an option without an argument. `-10` is interpreted as a
|
||||
## an option `-1` with the `0` argument.
|
||||
## + `Lax` and `Gnu` modes: `-10` is consumed as the value of `-c`
|
||||
## (allowing negative number values).
|
||||
##
|
||||
## **Long Options**
|
||||
##
|
||||
## Consider `--foo:bar`:
|
||||
##
|
||||
## - `Nim`: `:` is a valid delimiter, so `bar` is the value of `--foo`.
|
||||
## - `LaxMode`: same as `Nim`.
|
||||
## - `Gnu`: only `=` is a valid delimiter, so this parses as an option named
|
||||
## `foo:bar` without a value (unless `longNoVal` is non-empty and allows
|
||||
## next-argument consumption).
|
||||
##
|
||||
## Consider `--foo =bar`:
|
||||
##
|
||||
## - `Nim`: whitespace around delimiters is allowed, so `=bar` is the
|
||||
## value of `--foo`.
|
||||
## - `LaxMode`: same as `Nim`.
|
||||
## - `Gnu`: whitespace around `=` is not allowed, so `--foo` is an
|
||||
## option without a value, and `=bar` is parsed as an argument.
|
||||
##
|
||||
## Custom Rule Sets
|
||||
## ================
|
||||
##
|
||||
## .. Warning:: Custom rule sets are unsupported and not tested
|
||||
##
|
||||
## If you require parsing rules beyond the three provided modes, it's possible
|
||||
## to define a custom parser behavior by specifying a set of individual parser
|
||||
## rules.
|
||||
##
|
||||
## Due to this feature being unsupported, it requires importing the private
|
||||
## symbols of the module (with `import std/parseopt {.all.}`) and utilizing
|
||||
## the unexported `initOptParser` overload, which accepts `set[ParserRules]`
|
||||
## (see the `ParserRules` enum in the code for details).
|
||||
##
|
||||
## See also
|
||||
## ========
|
||||
@@ -171,13 +328,42 @@
|
||||
## parser
|
||||
## * `parsexml module<parsexml.html>`_ for a XML / HTML parser
|
||||
## * `other parsers<lib.html#pure-libraries-parsers>`_ for more parsers
|
||||
## * POSIX_ - The Open Group Base Specifications Issue 8. Utility Conventions
|
||||
## * GNU_ - GNU C Library reference manual. 26.1.1 Program Argument Syntax Conventions
|
||||
##
|
||||
## .. _GNU: https://sourceware.org/glibc/manual/latest/html_node/Argument-Syntax.html
|
||||
## .. _POSIX: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap12.html
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import std/strutils
|
||||
import std/os
|
||||
when defined(nimscript):
|
||||
from std/strutils import toLowerAscii, endsWith
|
||||
|
||||
type
|
||||
CliMode* = enum
|
||||
## Parser behavior profiles used to control parser behavior.
|
||||
## See `Parser Modes`_ for details.
|
||||
LaxMode, ## The most forgiving mode
|
||||
NimMode, ## Nim parsing rules (default)
|
||||
GnuMode ## GNU-style parsing
|
||||
|
||||
type
|
||||
ParserRules = enum
|
||||
## Feature flags used to assemble parser behavior for a given mode.
|
||||
prSepAllowDelimBefore, ## Allow whitespace before an opt-val separator
|
||||
prSepAllowDelimAfter, ## Allow whitespace after an opt-val separator
|
||||
prShortAllowSep, ## Allow `-k<separator>val` form
|
||||
prShortBundle, ## Allow bundling short options behind one '-'
|
||||
prShortValAllowAdjacent, ## Allow adjacent short option values: `-kval`
|
||||
prShortValAllowNextArg, ## Allow next-argv short option values: `-k val`
|
||||
prShortValAllowDashLeading, ## Allow values that start with '-' to be taken
|
||||
prLongAllowSep, ## Allow `--opt<separator>val` form
|
||||
prLongValAllowNextArg, ## Allow `--opt val` form, requires non-empty `longNoVal`
|
||||
prSepAllowColon, ## Allow `:` as an opt-val separator
|
||||
prSepAllowEq, ## Allow `=` as an opt-val separator
|
||||
|
||||
type
|
||||
CmdLineKind* = enum ## The detected command line token.
|
||||
@@ -189,21 +375,51 @@ type
|
||||
## Implementation of the command line parser.
|
||||
##
|
||||
## To initialize it, use the
|
||||
## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_.
|
||||
## `initOptParser proc<#initOptParser,string,set[char],seq[string],CliMode>`_.
|
||||
## `next<#next,OptParser>`_ is used to advance the parser state and move
|
||||
## through the parsed tokens.
|
||||
pos: int
|
||||
inShortState: bool
|
||||
allowWhitespaceAfterColon: bool
|
||||
shortNoVal: set[char]
|
||||
longNoVal: seq[string]
|
||||
cmds: seq[string]
|
||||
idx: int
|
||||
separators: set[char] ## Allowed separators for long/short option values
|
||||
rules: set[ParserRules]
|
||||
kind*: CmdLineKind ## The detected command line token
|
||||
key*, val*: string ## Key and value pair; the key is the option
|
||||
## or the argument, and the value is not "" if
|
||||
## the option was given a value
|
||||
|
||||
const DelimSet = {'\t', ' '} ## Allowed delimiters between tokens
|
||||
|
||||
func toRules(m: CliMode): set[ParserRules] =
|
||||
## Default rule sets for the given mode `m`
|
||||
let
|
||||
Common = {
|
||||
prSepAllowEq,
|
||||
prShortValAllowAdjacent,
|
||||
prShortBundle,
|
||||
prLongValAllowNextArg,
|
||||
prLongAllowSep,
|
||||
}
|
||||
Lax = {
|
||||
prSepAllowColon,
|
||||
prSepAllowDelimBefore,
|
||||
prSepAllowDelimAfter,
|
||||
prShortAllowSep,
|
||||
}
|
||||
ShortPosix = {
|
||||
prShortValAllowNextArg,
|
||||
prShortValAllowDashLeading,
|
||||
}
|
||||
case m
|
||||
of LaxMode: Common + Lax + ShortPosix
|
||||
of NimMode: Common + Lax
|
||||
of GnuMode: Common + ShortPosix
|
||||
|
||||
proc parseWord(s: string, i: int, w: var string,
|
||||
delim: set[char] = {'\t', ' '}): int =
|
||||
delim: set[char] = DelimSet): int =
|
||||
result = i
|
||||
if result < s.len and s[result] == '\"':
|
||||
inc(result)
|
||||
@@ -218,34 +434,23 @@ proc parseWord(s: string, i: int, w: var string,
|
||||
add(w, s[result])
|
||||
inc(result)
|
||||
|
||||
proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
|
||||
longNoVal: seq[string] = @[];
|
||||
allowWhitespaceAfterColon = true): OptParser =
|
||||
## Initializes the command line parser.
|
||||
##
|
||||
## If `cmdline.len == 0`, the real command line as provided by the
|
||||
## `os` module is retrieved instead if it is available. If the
|
||||
## command line is not available, a `ValueError` will be raised.
|
||||
## Behavior of the other parameters remains the same as in
|
||||
## `initOptParser(string, ...)
|
||||
## <#initOptParser,string,set[char],seq[string]>`_.
|
||||
##
|
||||
## See also:
|
||||
## * `getopt iterator<#getopt.i,seq[string],set[char],seq[string]>`_
|
||||
runnableExamples:
|
||||
var p = initOptParser()
|
||||
p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"])
|
||||
p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"],
|
||||
shortNoVal = {'l'}, longNoVal = @["left"])
|
||||
result = OptParser(pos: 0, idx: 0, inShortState: false,
|
||||
shortNoVal: shortNoVal, longNoVal: longNoVal,
|
||||
allowWhitespaceAfterColon: allowWhitespaceAfterColon
|
||||
proc initOptParser(cmdline: openArray[string];
|
||||
shortNoVal: set[char];
|
||||
longNoVal: seq[string];
|
||||
rules: set[ParserRules]): OptParser =
|
||||
result = OptParser(pos: 0, idx: 0,
|
||||
cmds: @cmdline,
|
||||
inShortState: false,
|
||||
shortNoVal: shortNoVal,
|
||||
longNoVal: longNoVal,
|
||||
separators: {},
|
||||
rules: rules,
|
||||
kind: cmdEnd,
|
||||
key: "", val: "",
|
||||
)
|
||||
if cmdline.len != 0:
|
||||
result.cmds = newSeq[string](cmdline.len)
|
||||
for i in 0..<cmdline.len:
|
||||
result.cmds[i] = cmdline[i]
|
||||
else:
|
||||
if prSepAllowEq in rules: result.separators.incl('=')
|
||||
if prSepAllowColon in rules: result.separators.incl(':')
|
||||
if cmdline.len == 0:
|
||||
when declared(paramCount):
|
||||
when defined(nimscript):
|
||||
var ctr = 0
|
||||
@@ -254,7 +459,7 @@ proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
|
||||
if firstNimsFound:
|
||||
result.cmds[ctr] = paramStr(i)
|
||||
inc ctr, 1
|
||||
if paramStr(i).endsWith(".nims") and not firstNimsFound:
|
||||
if paramStr(i).toLowerAscii().endsWith(".nims") and not firstNimsFound:
|
||||
firstNimsFound = true
|
||||
result.cmds = newSeq[string](paramCount()-i)
|
||||
else:
|
||||
@@ -266,25 +471,73 @@ proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
|
||||
# access the command line arguments then!
|
||||
raiseAssert "empty command line given but" &
|
||||
" real command line is not accessible"
|
||||
result.kind = cmdEnd
|
||||
result.key = ""
|
||||
result.val = ""
|
||||
|
||||
proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
|
||||
proc initOptParser*(cmdline: seq[string];
|
||||
shortNoVal: set[char] = {};
|
||||
longNoVal: seq[string] = @[];
|
||||
allowWhitespaceAfterColon = true): OptParser =
|
||||
mode: CliMode = NimMode): OptParser =
|
||||
## Initializes the command line parser.
|
||||
##
|
||||
## If `cmdline == ""`, the real command line as provided by the
|
||||
## `os` module is retrieved instead if it is available. If the
|
||||
## command line is not available, a `ValueError` will be raised.
|
||||
## **Parameters:**
|
||||
##
|
||||
## `shortNoVal` and `longNoVal` are used to specify which options
|
||||
## do not take values. See the `documentation about these
|
||||
## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
|
||||
## how this affects parsing.
|
||||
## - `cmdline`: Sequence of command line arguments to parse. If empty, the
|
||||
## real command line as provided by the `os` module is retrieved instead.
|
||||
## If the command line is not available, an assertion will be raised.
|
||||
## - `shortNoVal`: Set of short option characters that do not accept values.
|
||||
## See `shortNoVal and longNoVal<#nimshortnoval-and-nimlongnoval>`_ for details.
|
||||
## - `longNoVal`: Sequence of long option names that do not accept values.
|
||||
## See `shortNoVal and longNoVal<#nimshortnoval-and-nimlongnoval>`_ for details.
|
||||
## - `mode`: Parser behavior profile (`NimMode`, `LaxMode`, or `GnuMode`).
|
||||
## See `Parser Modes`_ for details.
|
||||
##
|
||||
## This does not provide a way of passing default values to arguments.
|
||||
## See also:
|
||||
## * `getopt iterator<#getopt.i,seq[string],set[char],seq[string],CliMode>`_
|
||||
runnableExamples:
|
||||
var p = initOptParser()
|
||||
p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"])
|
||||
p = initOptParser(@["--left", "--debug:3", "-l", "-r:2"],
|
||||
shortNoVal = {'l'}, longNoVal = @["left"])
|
||||
initOptParser(cmdline, shortNoVal, longNoVal, toRules(mode))
|
||||
|
||||
proc initOptParser*(cmdline: seq[string],
|
||||
shortNoVal: set[char] = {},
|
||||
longNoVal: seq[string] = @[];
|
||||
allowWhitespaceAfterColon: bool): OptParser {.deprecated:
|
||||
"`allowWhitespaceAfterColon` is deprecated, use parser modes instead".} =
|
||||
## This is an overload for continued support of the legacy `allowWhitespaceAfterColon`
|
||||
## option. It modifies the default parser mode so that the passed value is respected.
|
||||
##
|
||||
## Current default parser mode behaves as if `true` was passed (old default)
|
||||
##
|
||||
## - `allowWhitespaceAfterColon`: When `true`, allows forms like
|
||||
## `--option: value` or `--option= value` where the value is in the next
|
||||
## token after the delimiter. When `false`, the value must be in the same
|
||||
## token as the delimiter.
|
||||
var nimrules = toRules(NimMode)
|
||||
if allowWhitespaceAfterColon == false: nimrules.excl prSepAllowDelimAfter
|
||||
initOptParser(cmdline, shortNoVal, longNoVal, nimrules)
|
||||
|
||||
proc initOptParser*(cmdline = "";
|
||||
shortNoVal: set[char] = {};
|
||||
longNoVal: seq[string] = @[];
|
||||
mode: CliMode = NimMode): OptParser =
|
||||
## Initializes the command line parser from a command line string.
|
||||
##
|
||||
## The `cmdline` string is parsed into tokens using shell-like quoting rules.
|
||||
##
|
||||
## **Parameters:**
|
||||
##
|
||||
## - `cmdline`: Command line string to parse. If empty, the real command line
|
||||
## as provided by the `os` module is retrieved instead. If the command line
|
||||
## is not available, an assertion will be raised.
|
||||
## - `shortNoVal`: Set of short option characters that do not accept values.
|
||||
## See `shortNoVal and longNoVal<#nimshortnoval-and-nimlongnoval>`_ for details.
|
||||
## - `longNoVal`: Sequence of long option names that do not accept values.
|
||||
## See `shortNoVal and longNoVal<#nimshortnoval-and-nimlongnoval>`_ for details.
|
||||
## - `mode`: Parser behavior profile (`NimMode`, `LaxMode`, or `GnuMode`).
|
||||
## See `Parser Modes`_ for details.
|
||||
##
|
||||
## **Note:** This does not provide a way of passing default values to arguments.
|
||||
##
|
||||
## See also:
|
||||
## * `getopt iterator<#getopt.i,OptParser>`_
|
||||
@@ -293,34 +546,81 @@ proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
|
||||
p = initOptParser("--left --debug:3 -l -r:2")
|
||||
p = initOptParser("--left --debug:3 -l -r:2",
|
||||
shortNoVal = {'l'}, longNoVal = @["left"])
|
||||
initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, toRules(mode))
|
||||
|
||||
initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon)
|
||||
proc initOptParser*(cmdline = "";
|
||||
shortNoVal: set[char] = {};
|
||||
longNoVal: seq[string] = @[];
|
||||
allowWhitespaceAfterColon: bool): OptParser {.deprecated:
|
||||
"`allowWhitespaceAfterColon` is deprecated, use parser modes instead".} =
|
||||
## This is an overload for continued support of the legacy `allowWhitespaceAfterColon`
|
||||
## option. It modifies the default parser mode so that the passed value is respected.
|
||||
##
|
||||
## Current default parser mode behaves as if `true` was passed (old default).
|
||||
##
|
||||
## - `allowWhitespaceAfterColon`: When `true`, allows forms like
|
||||
## `--option: value` or `--option= value` where the value is in the next
|
||||
## token after the delimiter. When `false`, the value must be in the same
|
||||
## token as the delimiter.
|
||||
var nimrules = toRules(NimMode)
|
||||
if allowWhitespaceAfterColon == false: nimrules.excl prSepAllowDelimAfter
|
||||
initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, nimrules)
|
||||
|
||||
proc handleShortOption(p: var OptParser; cmd: string) =
|
||||
var i = p.pos
|
||||
p.kind = cmdShortOption
|
||||
if i < cmd.len:
|
||||
if i < cmd.len: # multidigit short option support goes here
|
||||
add(p.key, cmd[i])
|
||||
inc(i)
|
||||
p.inShortState = true
|
||||
while i < cmd.len and cmd[i] in {'\t', ' '}:
|
||||
inc(i)
|
||||
p.inShortState = false
|
||||
if i < cmd.len and (cmd[i] in {':', '='} or
|
||||
card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal):
|
||||
if i < cmd.len and cmd[i] in {':', '='}:
|
||||
if prSepAllowDelimBefore in p.rules:
|
||||
while i < cmd.len and cmd[i] in DelimSet:
|
||||
inc(i)
|
||||
p.inShortState = false
|
||||
|
||||
proc consumeDelims() =
|
||||
while i < cmd.len and cmd[i] in DelimSet: inc(i)
|
||||
|
||||
proc advance(p: var OptParser; n = 1)=
|
||||
p.inShortState = false
|
||||
while i < cmd.len and cmd[i] in {'\t', ' '}: inc(i)
|
||||
p.pos = 0
|
||||
inc p.idx, n
|
||||
|
||||
template next(): untyped = p.cmds[p.idx + 1]
|
||||
|
||||
let canTakeVal = card(p.shortNoVal) > 0 and p.key[0] notin p.shortNoVal
|
||||
if i < cmd.len and cmd[i] in p.separators:
|
||||
# separator case
|
||||
if prShortAllowSep in p.rules:
|
||||
# allow separators: skip the separator and take the value after it
|
||||
inc(i)
|
||||
if prSepAllowDelimAfter in p.rules:
|
||||
consumeDelims()
|
||||
# prohibit separators: treat separator + remainder as the value
|
||||
# this represents an error state but produces output that can be validated
|
||||
p.val = substr(cmd, i)
|
||||
p.pos = 0
|
||||
inc p.idx
|
||||
else:
|
||||
p.pos = i
|
||||
p.advance(1)
|
||||
return
|
||||
elif canTakeVal and prShortValAllowAdjacent in p.rules and i < cmd.len:
|
||||
# adjacent value
|
||||
if prSepAllowDelimBefore in p.rules:
|
||||
consumeDelims()
|
||||
p.val = substr(cmd, i)
|
||||
p.advance(1)
|
||||
return
|
||||
elif canTakeVal and
|
||||
prShortValAllowNextArg in p.rules and
|
||||
i >= cmd.len and
|
||||
p.idx + 1 < p.cmds.len and (
|
||||
prShortValAllowDashLeading in p.rules or
|
||||
not (next().len > 0 and next()[0] == '-')):
|
||||
# next-argument value
|
||||
p.val = next()
|
||||
p.advance(2)
|
||||
return
|
||||
p.pos = i
|
||||
if i >= cmd.len:
|
||||
p.inShortState = false
|
||||
p.pos = 0
|
||||
inc p.idx
|
||||
p.advance(1)
|
||||
|
||||
proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
|
||||
## Parses the next token.
|
||||
@@ -343,54 +643,71 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
|
||||
return
|
||||
|
||||
var i = p.pos
|
||||
while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
|
||||
template cmd(): untyped = p.cmds[p.idx]
|
||||
template nextArg(): untyped = p.cmds[p.idx + 1]
|
||||
|
||||
proc consumeDelims(cmds: openArray[string]; idx: int) =
|
||||
while i < cmds[idx].len and cmds[idx][i] in DelimSet: inc(i)
|
||||
|
||||
proc advance(p: var OptParser; n = 1) =
|
||||
p.pos = 0
|
||||
inc p.idx, n
|
||||
|
||||
consumeDelims(p.cmds, p.idx)
|
||||
p.pos = i
|
||||
setLen(p.key, 0)
|
||||
setLen(p.val, 0)
|
||||
if p.inShortState:
|
||||
p.inShortState = false
|
||||
if i >= p.cmds[p.idx].len:
|
||||
inc(p.idx)
|
||||
p.pos = 0
|
||||
if i < cmd.len:
|
||||
handleShortOption(p, p.cmds[p.idx])
|
||||
return
|
||||
else:
|
||||
p.advance(1)
|
||||
if p.idx >= p.cmds.len:
|
||||
p.kind = cmdEnd
|
||||
return
|
||||
else:
|
||||
handleShortOption(p, p.cmds[p.idx])
|
||||
return
|
||||
|
||||
if i < p.cmds[p.idx].len and p.cmds[p.idx][i] == '-':
|
||||
if i < cmd.len and cmd[i] == '-':
|
||||
inc(i)
|
||||
if i < p.cmds[p.idx].len and p.cmds[p.idx][i] == '-':
|
||||
if i < cmd.len and cmd[i] == '-':
|
||||
p.kind = cmdLongOption
|
||||
inc(i)
|
||||
i = parseWord(p.cmds[p.idx], i, p.key, {' ', '\t', ':', '='})
|
||||
while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
|
||||
if i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {':', '='}:
|
||||
i = parseWord(cmd, i, p.key,
|
||||
DelimSet + (if prLongAllowSep in p.rules: p.separators else: {}))
|
||||
if prSepAllowDelimBefore in p.rules:
|
||||
consumeDelims(p.cmds, p.idx)
|
||||
if prLongAllowSep in p.rules and i < cmd.len and cmd[i] in p.separators:
|
||||
inc(i)
|
||||
while i < p.cmds[p.idx].len and p.cmds[p.idx][i] in {'\t', ' '}: inc(i)
|
||||
# if we're at the end, use the next command line option:
|
||||
if i >= p.cmds[p.idx].len and p.idx < p.cmds.len and
|
||||
p.allowWhitespaceAfterColon:
|
||||
inc p.idx
|
||||
i = 0
|
||||
if p.idx < p.cmds.len:
|
||||
p.val = p.cmds[p.idx].substr(i)
|
||||
elif len(p.longNoVal) > 0 and p.key notin p.longNoVal and p.idx+1 < p.cmds.len:
|
||||
p.val = p.cmds[p.idx+1]
|
||||
inc p.idx
|
||||
if prSepAllowDelimAfter in p.rules:
|
||||
consumeDelims(p.cmds, p.idx)
|
||||
if i >= cmd.len and p.idx + 1 < p.cmds.len and
|
||||
prSepAllowDelimAfter in p.rules:
|
||||
p.val = nextArg()
|
||||
p.advance(2)
|
||||
else:
|
||||
p.val = cmd.substr(i)
|
||||
p.advance(1)
|
||||
elif prLongValAllowNextArg in p.rules and
|
||||
len(p.longNoVal) > 0 and
|
||||
p.key notin p.longNoVal and
|
||||
p.idx + 1 < p.cmds.len:
|
||||
p.val = nextArg()
|
||||
p.advance(2)
|
||||
else:
|
||||
p.val = ""
|
||||
inc p.idx
|
||||
p.pos = 0
|
||||
if i < cmd.len:
|
||||
# Leave remainder of the current token to be parsed as an argument.
|
||||
consumeDelims(p.cmds, p.idx)
|
||||
p.cmds[p.idx] = cmd.substr(i)
|
||||
else:
|
||||
p.advance(1)
|
||||
else:
|
||||
p.pos = i
|
||||
handleShortOption(p, p.cmds[p.idx])
|
||||
handleShortOption(p, cmd)
|
||||
else:
|
||||
p.kind = cmdArgument
|
||||
p.key = p.cmds[p.idx]
|
||||
inc p.idx
|
||||
p.pos = 0
|
||||
p.key = cmd
|
||||
p.advance(1)
|
||||
|
||||
when declared(quoteShellCommand):
|
||||
proc cmdLineRest*(p: OptParser): string {.rtl, extern: "npo$1".} =
|
||||
@@ -399,15 +716,13 @@ when declared(quoteShellCommand):
|
||||
## See also:
|
||||
## * `remainingArgs proc<#remainingArgs,OptParser>`_
|
||||
##
|
||||
## **Examples:**
|
||||
## ```Nim
|
||||
## var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
|
||||
## while true:
|
||||
## p.next()
|
||||
## if p.kind == cmdLongOption and p.key == "": # Look for "--"
|
||||
## break
|
||||
## doAssert p.cmdLineRest == "foo.txt bar.txt"
|
||||
## ```
|
||||
runnableExamples:
|
||||
var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
|
||||
while true:
|
||||
p.next()
|
||||
if p.kind == cmdLongOption and p.key == "": # Look for "--"
|
||||
break
|
||||
doAssert p.cmdLineRest == "foo.txt bar.txt"
|
||||
result = p.cmds[p.idx .. ^1].quoteShellCommand
|
||||
|
||||
proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} =
|
||||
@@ -416,15 +731,13 @@ proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} =
|
||||
## See also:
|
||||
## * `cmdLineRest proc<#cmdLineRest,OptParser>`_
|
||||
##
|
||||
## **Examples:**
|
||||
## ```Nim
|
||||
## var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
|
||||
## while true:
|
||||
## p.next()
|
||||
## if p.kind == cmdLongOption and p.key == "": # Look for "--"
|
||||
## break
|
||||
## doAssert p.remainingArgs == @["foo.txt", "bar.txt"]
|
||||
## ```
|
||||
runnableExamples:
|
||||
var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
|
||||
while true:
|
||||
p.next()
|
||||
if p.kind == cmdLongOption and p.key == "": # Look for "--"
|
||||
break
|
||||
doAssert p.remainingArgs == @["foo.txt", "bar.txt"]
|
||||
result = @[]
|
||||
for i in p.idx..<p.cmds.len: result.add p.cmds[i]
|
||||
|
||||
@@ -439,29 +752,26 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
|
||||
## See also:
|
||||
## * `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_
|
||||
##
|
||||
## **Examples:**
|
||||
##
|
||||
## ```Nim
|
||||
## # these are placeholders, of course
|
||||
## proc writeHelp() = discard
|
||||
## proc writeVersion() = discard
|
||||
##
|
||||
## var filename: string
|
||||
## var p = initOptParser("--left --debug:3 -l -r:2")
|
||||
##
|
||||
## for kind, key, val in p.getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## filename = key
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key
|
||||
## of "help", "h": writeHelp()
|
||||
## of "version", "v": writeVersion()
|
||||
## of cmdEnd: assert(false) # cannot happen
|
||||
## if filename == "":
|
||||
## # no filename has been given, so we show the help
|
||||
## writeHelp()
|
||||
## ```
|
||||
runnableExamples:
|
||||
# these are placeholders, of course
|
||||
proc writeHelp() = discard
|
||||
proc writeVersion() = discard
|
||||
|
||||
var filename: string = ""
|
||||
var p = initOptParser("--left --debug:3 -l -r:2")
|
||||
|
||||
for kind, key, val in p.getopt():
|
||||
case kind
|
||||
of cmdArgument:
|
||||
filename = key
|
||||
of cmdLongOption, cmdShortOption:
|
||||
case key
|
||||
of "help", "h": writeHelp()
|
||||
of "version", "v": writeVersion()
|
||||
of cmdEnd: assert(false) # cannot happen
|
||||
if filename == "":
|
||||
# no filename has been given, so we show the help
|
||||
writeHelp()
|
||||
p.pos = 0
|
||||
p.idx = 0
|
||||
while true:
|
||||
@@ -469,8 +779,10 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
|
||||
if p.kind == cmdEnd: break
|
||||
yield (p.kind, p.key, p.val)
|
||||
|
||||
iterator getopt*(cmdline: seq[string] = @[],
|
||||
shortNoVal: set[char] = {}, longNoVal: seq[string] = @[]):
|
||||
iterator getopt*(cmdline: seq[string] = @[];
|
||||
shortNoVal: set[char] = {};
|
||||
longNoVal: seq[string] = @[];
|
||||
mode: CliMode = NimMode):
|
||||
tuple[kind: CmdLineKind, key, val: string] =
|
||||
## Convenience iterator for iterating over command line arguments.
|
||||
##
|
||||
@@ -483,6 +795,9 @@ iterator getopt*(cmdline: seq[string] = @[],
|
||||
## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
|
||||
## how this affects parsing.
|
||||
##
|
||||
## `mode` selects the parser behavior profile (`NimMode`, `LaxMode`,
|
||||
## or `GnuMode`). See `Parser Modes`_ for details.
|
||||
##
|
||||
## There is no need to check for `cmdEnd` while iterating. If using `getopt`
|
||||
## with case switching, checking for `cmdEnd` is required.
|
||||
##
|
||||
@@ -513,7 +828,8 @@ iterator getopt*(cmdline: seq[string] = @[],
|
||||
## writeHelp()
|
||||
## ```
|
||||
var p = initOptParser(cmdline, shortNoVal = shortNoVal,
|
||||
longNoVal = longNoVal)
|
||||
longNoVal = longNoVal,
|
||||
rules = toRules(mode))
|
||||
while true:
|
||||
next(p)
|
||||
if p.kind == cmdEnd: break
|
||||
|
||||
@@ -19,10 +19,12 @@ include "system/inclrtl"
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
{.push gcsafe.}
|
||||
|
||||
const
|
||||
useUnicode = true ## change this to deactivate proper UTF-8 support
|
||||
|
||||
import std/[strutils, macros]
|
||||
import std/[strformat, strutils, macros]
|
||||
import std/private/decode_helpers
|
||||
|
||||
when useUnicode:
|
||||
@@ -562,8 +564,10 @@ template matchOrParse(mopProc: untyped) =
|
||||
# procs. For the former, *enter* and *leave* event handler code generators
|
||||
# are provided which just return *discard*.
|
||||
|
||||
proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe, raises: [].} =
|
||||
proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int =
|
||||
proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.raises: [].} =
|
||||
result = 0
|
||||
|
||||
proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int {.raises: [].}=
|
||||
# Parse handler code must run in an *of* clause of its own for each
|
||||
# *PegKind*, so we encapsulate the identical clause body for
|
||||
# *pkBackRef..pkBackRefIgnoreStyle* here.
|
||||
@@ -1029,7 +1033,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
|
||||
## Symbols declared in an *enter* handler can be made visible in the
|
||||
## corresponding *leave* handler by annotating them with an *inject* pragma.
|
||||
proc rawParse(s: string, p: Peg, start: int, c: var Captures): int
|
||||
{.gensym.} =
|
||||
{.gensym, raises: [ValueError].} =
|
||||
|
||||
# binding from *macros*
|
||||
bind strVal
|
||||
@@ -1295,7 +1299,7 @@ when not defined(nimHasEffectsOf):
|
||||
{.pragma: effectsOf.}
|
||||
|
||||
func replace*(s: string, sub: Peg, cb: proc(
|
||||
match: int, cnt: int, caps: openArray[string]): string): string {.
|
||||
match: int, cnt: int, caps: openArray[string]): string {.gcsafe.}): string {.
|
||||
rtl, extern: "npegs$1cb", effectsOf: cb.} =
|
||||
## Replaces `sub` in `s` by the resulting strings from the callback.
|
||||
## The callback proc receives the index of the current match (starting with 0),
|
||||
@@ -1341,7 +1345,7 @@ func replace*(s: string, sub: Peg, cb: proc(
|
||||
when not defined(js):
|
||||
proc transformFile*(infile, outfile: string,
|
||||
subs: varargs[tuple[pattern: Peg, repl: string]]) {.
|
||||
rtl, extern: "npegs$1".} =
|
||||
rtl, extern: "npegs$1", raises: [ValueError, IOError].} =
|
||||
## reads in the file `infile`, performs a parallel replacement (calls
|
||||
## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
|
||||
## error occurs. This is supposed to be used for quick scripting.
|
||||
@@ -1480,9 +1484,9 @@ func getLine(L: PegLexer): int {.inline.} =
|
||||
result = L.lineNumber
|
||||
|
||||
func errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
|
||||
var line = if line < 0: getLine(L) else: line
|
||||
var col = if col < 0: getColumn(L) else: col
|
||||
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
|
||||
let line = if line < 0: getLine(L) else: line
|
||||
let col = if col < 0: getColumn(L) else: col
|
||||
&"{L.filename}({line}, {col}) Error: {msg}"
|
||||
|
||||
func getEscapedChar(c: var PegLexer, tok: var Token) =
|
||||
inc(c.bufpos)
|
||||
@@ -1677,7 +1681,7 @@ func getBuiltin(c: var PegLexer, tok: var Token) =
|
||||
tok.kind = tkEscaped
|
||||
getEscapedChar(c, tok) # may set tok.kind to tkInvalid
|
||||
|
||||
func getTok(c: var PegLexer, tok: var Token) =
|
||||
func getTok(c: var PegLexer, tok: var Token) {.raises: [].} =
|
||||
tok.kind = tkInvalid
|
||||
tok.modifier = modNone
|
||||
setLen(tok.literal, 0)
|
||||
@@ -1820,11 +1824,10 @@ type
|
||||
identIsVerbatim: bool
|
||||
skip: Peg
|
||||
|
||||
func pegError(p: PegParser, msg: string, line = -1, col = -1) {.noreturn.} =
|
||||
var e = (ref EInvalidPeg)(msg: errorStr(p, msg, line, col))
|
||||
raise e
|
||||
func pegError(p: PegParser, msg: string, line = -1, col = -1) {.noreturn, raises: [EInvalidPeg].} =
|
||||
raise (ref EInvalidPeg)(msg: errorStr(p, msg, line, col))
|
||||
|
||||
func getTok(p: var PegParser) =
|
||||
func getTok(p: var PegParser) {.raises: [EInvalidPeg].}=
|
||||
getTok(p, p.tok)
|
||||
if p.tok.kind == tkInvalid: pegError(p, "'" & p.tok.literal & "' is invalid token")
|
||||
|
||||
@@ -1832,7 +1835,7 @@ func eat(p: var PegParser, kind: TokKind) =
|
||||
if p.tok.kind == kind: getTok(p)
|
||||
else: pegError(p, tokKindToStr[kind] & " expected")
|
||||
|
||||
func parseExpr(p: var PegParser): Peg {.gcsafe.}
|
||||
func parseExpr(p: var PegParser): Peg {.raises: [EInvalidPeg].}
|
||||
|
||||
func getNonTerminal(p: var PegParser, name: string): NonTerminal =
|
||||
for i in 0..high(p.nonterms):
|
||||
@@ -1881,7 +1884,7 @@ func token(terminal: Peg, p: PegParser): Peg =
|
||||
if p.skip.kind == pkEmpty: result = terminal
|
||||
else: result = sequence(p.skip, terminal)
|
||||
|
||||
func primary(p: var PegParser): Peg =
|
||||
func primary(p: var PegParser): Peg {.raises: [EInvalidPeg].}=
|
||||
case p.tok.kind
|
||||
of tkAmp:
|
||||
getTok(p)
|
||||
@@ -1974,7 +1977,7 @@ func primary(p: var PegParser): Peg =
|
||||
getTok(p)
|
||||
else: break
|
||||
|
||||
func seqExpr(p: var PegParser): Peg =
|
||||
func seqExpr(p: var PegParser): Peg {.raises: [EInvalidPeg].}=
|
||||
result = primary(p)
|
||||
while true:
|
||||
case p.tok.kind
|
||||
@@ -2040,7 +2043,7 @@ func rawParse(p: var PegParser): Peg =
|
||||
elif ntUsed notin nt.flags and i > 0:
|
||||
pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
|
||||
|
||||
func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
|
||||
func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg {.raises: [EInvalidPeg].} =
|
||||
## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
|
||||
## used for error messages, but they only provide start offsets. `parsePeg`
|
||||
## keeps track of line and column numbers within `pattern`.
|
||||
@@ -2055,7 +2058,7 @@ func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
|
||||
getTok(p)
|
||||
result = rawParse(p)
|
||||
|
||||
func peg*(pattern: string): Peg =
|
||||
func peg*(pattern: string): Peg {.raises: [EInvalidPeg].} =
|
||||
## constructs a Peg object from the `pattern`. The short name has been
|
||||
## chosen to encourage its use as a raw string modifier:
|
||||
##
|
||||
|
||||
@@ -242,7 +242,7 @@ proc rand[T: uint | uint64](r: var Rand; max: T): T =
|
||||
else:
|
||||
inc iters
|
||||
|
||||
proc rand*(r: var Rand; max: Natural): int {.benign.} =
|
||||
proc rand*(r: var Rand; max: Natural): int {.gcsafe.} =
|
||||
## Returns a random integer in the range `0..max` using the given state.
|
||||
##
|
||||
## **See also:**
|
||||
@@ -259,7 +259,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} =
|
||||
cast[int](rand(r, uint64(max)))
|
||||
# xxx toUnsigned pending https://github.com/nim-lang/Nim/pull/18445
|
||||
|
||||
proc rand*(max: int): int {.benign.} =
|
||||
proc rand*(max: int): int {.gcsafe.} =
|
||||
## Returns a random integer in the range `0..max`.
|
||||
##
|
||||
## If `randomize <#randomize>`_ has not been called, the sequence of random
|
||||
@@ -280,7 +280,7 @@ proc rand*(max: int): int {.benign.} =
|
||||
|
||||
rand(state, max)
|
||||
|
||||
proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
|
||||
proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.gcsafe.} =
|
||||
## Returns a random floating point number in the range `0.0..max`
|
||||
## using the given state.
|
||||
##
|
||||
@@ -307,7 +307,7 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
|
||||
let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
|
||||
result = (cast[float](u) - 1.0) * max
|
||||
|
||||
proc rand*(max: float): float {.benign.} =
|
||||
proc rand*(max: float): float {.gcsafe.} =
|
||||
## Returns a random floating point number in the range `0.0..max`.
|
||||
##
|
||||
## If `randomize <#randomize>`_ has not been called, the sequence of random
|
||||
@@ -611,7 +611,7 @@ proc initRand*(seed: int64): Rand =
|
||||
skipRandomNumbers(result)
|
||||
discard next(result)
|
||||
|
||||
proc randomize*(seed: int64) {.benign.} =
|
||||
proc randomize*(seed: int64) {.gcsafe.} =
|
||||
## Initializes the default random number generator with the given seed.
|
||||
##
|
||||
## Providing a specific seed will produce the same results for that seed each time.
|
||||
@@ -735,7 +735,7 @@ when not defined(standalone):
|
||||
since (1, 5, 1):
|
||||
export initRand
|
||||
|
||||
proc randomize*() {.benign.} =
|
||||
proc randomize*() {.gcsafe.} =
|
||||
## Initializes the default random number generator with a seed based on
|
||||
## random number source.
|
||||
##
|
||||
|
||||
@@ -909,6 +909,7 @@ when defined(windows):
|
||||
## `true` otherwise.
|
||||
password.setLen(0)
|
||||
stdout.write(prompt)
|
||||
stdout.flushFile()
|
||||
let hi = createFileA("CONIN$",
|
||||
GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0)
|
||||
var mode = DWORD 0
|
||||
@@ -936,6 +937,7 @@ else:
|
||||
cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
|
||||
discard fd.tcSetAttr(TCSADRAIN, cur.addr)
|
||||
stdout.write prompt
|
||||
stdout.flushFile()
|
||||
result = stdin.readLine(password)
|
||||
stdout.write "\n"
|
||||
discard fd.tcSetAttr(TCSADRAIN, old.addr)
|
||||
|
||||
@@ -382,9 +382,9 @@ type
|
||||
## timezones. The `times` module only supplies implementations for the
|
||||
## system's local time and UTC.
|
||||
zonedTimeFromTimeImpl: proc (x: Time): ZonedTime
|
||||
{.tags: [], raises: [], benign.}
|
||||
{.tags: [], raises: [], gcsafe.}
|
||||
zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime
|
||||
{.tags: [], raises: [], benign.}
|
||||
{.tags: [], raises: [], gcsafe.}
|
||||
name: string
|
||||
|
||||
ZonedTime* = object ## Represents a point in time with an associated
|
||||
@@ -432,7 +432,7 @@ else:
|
||||
# Helper procs
|
||||
#
|
||||
|
||||
{.pragma: operator, rtl, noSideEffect, benign.}
|
||||
{.pragma: operator, rtl, noSideEffect, gcsafe.}
|
||||
|
||||
proc convert*[T: SomeInteger](unitFrom, unitTo: FixedTimeUnit, quantity: T): T
|
||||
{.inline.} =
|
||||
@@ -518,7 +518,7 @@ proc fromEpochDay(epochday: int64):
|
||||
return (d.MonthdayRange, m.Month, (y + ord(m <= 2)).int)
|
||||
|
||||
proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int):
|
||||
YeardayRange {.tags: [], raises: [], benign.} =
|
||||
YeardayRange {.tags: [], raises: [], gcsafe.} =
|
||||
## Returns the day of the year.
|
||||
## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).yearday`.
|
||||
runnableExamples:
|
||||
@@ -538,7 +538,7 @@ proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int):
|
||||
result = daysUntilMonth[month] + monthday - 1
|
||||
|
||||
proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay
|
||||
{.tags: [], raises: [], benign.} =
|
||||
{.tags: [], raises: [], gcsafe.} =
|
||||
## Returns the day of the week enum from day, month and year.
|
||||
## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).weekday`.
|
||||
runnableExamples:
|
||||
@@ -922,21 +922,21 @@ proc nanosecond*(time: Time): NanosecondRange =
|
||||
time.nanosecond
|
||||
|
||||
proc fromUnix*(unix: int64): Time
|
||||
{.benign, tags: [], raises: [], noSideEffect.} =
|
||||
{.gcsafe, tags: [], raises: [], noSideEffect.} =
|
||||
## Convert a unix timestamp (seconds since `1970-01-01T00:00:00Z`)
|
||||
## to a `Time`.
|
||||
runnableExamples:
|
||||
doAssert $fromUnix(0).utc == "1970-01-01T00:00:00Z"
|
||||
initTime(unix, 0)
|
||||
|
||||
proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} =
|
||||
proc toUnix*(t: Time): int64 {.gcsafe, tags: [], raises: [], noSideEffect.} =
|
||||
## Convert `t` to a unix timestamp (seconds since `1970-01-01T00:00:00Z`).
|
||||
## See also `toUnixFloat` for subsecond resolution.
|
||||
runnableExamples:
|
||||
doAssert fromUnix(0).toUnix() == 0
|
||||
t.seconds
|
||||
|
||||
proc fromUnixFloat(seconds: float): Time {.benign, tags: [], raises: [], noSideEffect.} =
|
||||
proc fromUnixFloat(seconds: float): Time {.gcsafe, tags: [], raises: [], noSideEffect.} =
|
||||
## Convert a unix timestamp in seconds to a `Time`; same as `fromUnix`
|
||||
## but with subsecond resolution.
|
||||
runnableExamples:
|
||||
@@ -946,7 +946,7 @@ proc fromUnixFloat(seconds: float): Time {.benign, tags: [], raises: [], noSideE
|
||||
let nsecs = (seconds - secs) * 1e9
|
||||
initTime(secs.int64, nsecs.NanosecondRange)
|
||||
|
||||
proc toUnixFloat(t: Time): float {.benign, tags: [], raises: [].} =
|
||||
proc toUnixFloat(t: Time): float {.gcsafe, tags: [], raises: [].} =
|
||||
## Same as `toUnix` but using subsecond resolution.
|
||||
runnableExamples:
|
||||
let t = getTime()
|
||||
@@ -975,7 +975,7 @@ proc toWinTime*(t: Time): int64 =
|
||||
proc getTimeImpl(typ: typedesc[Time]): Time =
|
||||
raiseAssert "implemented in the vm"
|
||||
|
||||
proc getTime*(): Time {.tags: [TimeEffect], benign.} =
|
||||
proc getTime*(): Time {.tags: [TimeEffect], gcsafe.} =
|
||||
## Gets the current time as a `Time` with up to nanosecond resolution.
|
||||
when nimvm:
|
||||
result = getTimeImpl(Time)
|
||||
@@ -1154,7 +1154,7 @@ proc isLeapDay*(dt: DateTime): bool {.since: (1, 1).} =
|
||||
assertDateTimeInitialized dt
|
||||
dt.year.isLeapYear and dt.month == mFeb and dt.monthday == 29
|
||||
|
||||
proc toTime*(dt: DateTime): Time {.tags: [], raises: [], benign.} =
|
||||
proc toTime*(dt: DateTime): Time {.tags: [], raises: [], gcsafe.} =
|
||||
## Converts a `DateTime` to a `Time` representing the same point in time.
|
||||
assertDateTimeInitialized dt
|
||||
let epochDay = toEpochDay(dt.monthday, dt.month, dt.year)
|
||||
@@ -1197,9 +1197,9 @@ proc initDateTime(zt: ZonedTime, zone: Timezone): DateTime =
|
||||
proc newTimezone*(
|
||||
name: string,
|
||||
zonedTimeFromTimeImpl: proc (time: Time): ZonedTime
|
||||
{.tags: [], raises: [], benign.},
|
||||
{.tags: [], raises: [], gcsafe.},
|
||||
zonedTimeFromAdjTimeImpl: proc (adjTime: Time): ZonedTime
|
||||
{.tags: [], raises: [], benign.}
|
||||
{.tags: [], raises: [], gcsafe.}
|
||||
): owned Timezone =
|
||||
## Create a new `Timezone`.
|
||||
##
|
||||
@@ -1263,12 +1263,12 @@ proc `==`*(zone1, zone2: Timezone): bool =
|
||||
zone1.name == zone2.name
|
||||
|
||||
proc inZone*(time: Time, zone: Timezone): DateTime
|
||||
{.tags: [], raises: [], benign.} =
|
||||
{.tags: [], raises: [], gcsafe.} =
|
||||
## Convert `time` into a `DateTime` using `zone` as the timezone.
|
||||
result = initDateTime(zone.zonedTimeFromTime(time), zone)
|
||||
|
||||
proc inZone*(dt: DateTime, zone: Timezone): DateTime
|
||||
{.tags: [], raises: [], benign.} =
|
||||
{.tags: [], raises: [], gcsafe.} =
|
||||
## Returns a `DateTime` representing the same point in time as `dt` but
|
||||
## using `zone` as the timezone.
|
||||
assertDateTimeInitialized dt
|
||||
@@ -1283,14 +1283,14 @@ proc toAdjTime(dt: DateTime): Time =
|
||||
result = initTime(seconds, dt.nanosecond)
|
||||
|
||||
when defined(js):
|
||||
proc localZonedTimeFromTime(time: Time): ZonedTime {.benign.} =
|
||||
proc localZonedTimeFromTime(time: Time): ZonedTime {.gcsafe.} =
|
||||
let jsDate = newDate(time.seconds * 1000)
|
||||
let offset = jsDate.getTimezoneOffset() * secondsInMin
|
||||
result.time = time
|
||||
result.utcOffset = offset
|
||||
result.isDst = false
|
||||
|
||||
proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.benign.} =
|
||||
proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.gcsafe.} =
|
||||
let utcDate = newDate(adjTime.seconds * 1000)
|
||||
let localDate = newDate(utcDate.getUTCFullYear(), utcDate.getUTCMonth(),
|
||||
utcDate.getUTCDate(), utcDate.getUTCHours(), utcDate.getUTCMinutes(),
|
||||
@@ -1337,11 +1337,11 @@ else:
|
||||
return ((a.int64 - tm.toAdjUnix).int, tm.tm_isdst > 0)
|
||||
return (0, false)
|
||||
|
||||
proc localZonedTimeFromTime(time: Time): ZonedTime {.benign.} =
|
||||
proc localZonedTimeFromTime(time: Time): ZonedTime {.gcsafe.} =
|
||||
let (offset, dst) = getLocalOffsetAndDst(time.seconds)
|
||||
result = ZonedTime(time: time, utcOffset: offset, isDst: dst)
|
||||
|
||||
proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.benign.} =
|
||||
proc localZonedTimeFromAdjTime(adjTime: Time): ZonedTime {.gcsafe.} =
|
||||
var adjUnix = adjTime.seconds
|
||||
let past = adjUnix - secondsInDay
|
||||
let (pastOffset, _) = getLocalOffsetAndDst(past)
|
||||
@@ -1408,7 +1408,7 @@ proc local*(t: Time): DateTime =
|
||||
## Shorthand for `t.inZone(local())`.
|
||||
t.inZone(local())
|
||||
|
||||
proc now*(): DateTime {.tags: [TimeEffect], benign.} =
|
||||
proc now*(): DateTime {.tags: [TimeEffect], gcsafe.} =
|
||||
## Get the current time as a `DateTime` in the local timezone.
|
||||
## Shorthand for `getTime().local`.
|
||||
##
|
||||
@@ -2327,7 +2327,7 @@ proc parseTime*(input: string, f: static[string], zone: Timezone): Time
|
||||
const f2 = initTimeFormat(f)
|
||||
result = input.parse(f2, zone).toTime()
|
||||
|
||||
proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
|
||||
proc `$`*(dt: DateTime): string {.tags: [], raises: [], gcsafe.} =
|
||||
## Converts a `DateTime` object to a string representation.
|
||||
## It uses the format `yyyy-MM-dd'T'HH:mm:sszzz`.
|
||||
runnableExamples:
|
||||
@@ -2339,7 +2339,7 @@ proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
|
||||
else:
|
||||
result = format(dt, "yyyy-MM-dd'T'HH:mm:sszzz")
|
||||
|
||||
proc `$`*(time: Time): string {.tags: [], raises: [], benign.} =
|
||||
proc `$`*(time: Time): string {.tags: [], raises: [], gcsafe.} =
|
||||
## Converts a `Time` value to a string representation. It will use the local
|
||||
## time zone and use the format `yyyy-MM-dd'T'HH:mm:sszzz`.
|
||||
runnableExamples:
|
||||
|
||||
@@ -1037,6 +1037,19 @@ proc split*(s: openArray[char], sep: Rune, maxsplit: int = -1): seq[string] {.no
|
||||
## that returns a sequence of substrings.
|
||||
accResult(split(s, sep, maxsplit))
|
||||
|
||||
func getRuneHeadIdx(s: openArray[char], idx: int): int =
|
||||
## Given `[idx]` is within a Rune, then `s[result]` is the first byte of that Rune.
|
||||
result = idx
|
||||
if s[result] <= '\x7F': # 0b0111_1111
|
||||
return
|
||||
# 0b1...
|
||||
dec result
|
||||
for _ in 0..1:
|
||||
if s[result] >= '\xC0': # 0b11xx_xxxx
|
||||
# 0b110... or 0b1110...
|
||||
return
|
||||
dec result
|
||||
|
||||
proc strip*(s: openArray[char], leading = true, trailing = true,
|
||||
runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect,
|
||||
rtl, extern: "nucStrip".} =
|
||||
@@ -1073,18 +1086,9 @@ proc strip*(s: openArray[char], leading = true, trailing = true,
|
||||
xI: int
|
||||
rune: Rune
|
||||
while i >= 0:
|
||||
i = getRuneHeadIdx(s, i)
|
||||
xI = i
|
||||
fastRuneAt(s, xI, rune)
|
||||
var yI = i - 1
|
||||
while yI >= 0:
|
||||
var
|
||||
yIend = yI
|
||||
pRune: Rune
|
||||
fastRuneAt(s, yIend, pRune)
|
||||
if yIend < xI: break
|
||||
i = yI
|
||||
rune = pRune
|
||||
dec(yI)
|
||||
if not runes.contains(rune):
|
||||
eI = xI - 1
|
||||
break
|
||||
|
||||
@@ -547,14 +547,11 @@ template test*(name, body) {.dirty.} =
|
||||
for formatter in formatters:
|
||||
formatter.testStarted(name)
|
||||
|
||||
{.push warning[BareExcept]:off.}
|
||||
try:
|
||||
when declared(testSetupIMPLFlag): testSetupIMPL()
|
||||
when declared(testTeardownIMPLFlag):
|
||||
defer: testTeardownIMPL()
|
||||
{.push warning[BareExcept]:on.}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
except Exception:
|
||||
let e = getCurrentException()
|
||||
@@ -577,7 +574,6 @@ template test*(name, body) {.dirty.} =
|
||||
)
|
||||
testEnded(testResult)
|
||||
checkpoints = @[]
|
||||
{.pop.}
|
||||
|
||||
proc checkpoint*(msg: string) =
|
||||
## Set a checkpoint identified by `msg`. Upon test failure all
|
||||
@@ -803,11 +799,8 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
|
||||
discard
|
||||
|
||||
template expectBody(errorTypes, lineInfoLit, body): NimNode {.dirty.} =
|
||||
{.push warning[BareExcept]:off.}
|
||||
try:
|
||||
{.push warning[BareExcept]:on.}
|
||||
body
|
||||
{.pop.}
|
||||
checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
|
||||
fail()
|
||||
except errorTypes:
|
||||
@@ -816,8 +809,6 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
|
||||
let err = getCurrentException()
|
||||
checkpoint(lineInfoLit & ": Expect Failed, " & $err.name & " was thrown.")
|
||||
fail()
|
||||
{.pop.}
|
||||
|
||||
var errorTypes = newNimNode(nnkBracket)
|
||||
var hasException = false
|
||||
for exp in exceptions:
|
||||
|
||||
@@ -19,12 +19,16 @@ import std/private/miscdollars
|
||||
|
||||
type InstantiationInfo = tuple[filename: string, line: int, column: int]
|
||||
|
||||
{.push overflowChecks: off, rangeChecks: off.}
|
||||
|
||||
proc `$`(info: InstantiationInfo): string =
|
||||
# The +1 is needed here
|
||||
# instead of overriding `$` (and changing its meaning), consider explicit name.
|
||||
result = ""
|
||||
result.toLocation(info.filename, info.line, info.column + 1)
|
||||
|
||||
{.pop.}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
|
||||
of nnkEnumFieldDef:
|
||||
fVal = f[0].strVal
|
||||
case f[1].kind
|
||||
of nnkStrLit:
|
||||
of nnkStrLit .. nnkTripleStrLit:
|
||||
fStr = f[1].strVal
|
||||
of nnkTupleConstr:
|
||||
fStr = f[1][1].strVal
|
||||
@@ -57,7 +57,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
|
||||
fNum = f[1].intVal
|
||||
else:
|
||||
let fAst = f[0].getImpl
|
||||
if fAst.kind == nnkStrLit:
|
||||
if fAst.kind in {nnkStrLit .. nnkTripleStrLit}:
|
||||
fStr = fAst.strVal
|
||||
else:
|
||||
error("Invalid tuple syntax!", f[1])
|
||||
|
||||
@@ -29,18 +29,23 @@ const
|
||||
# doAssert res == digits100
|
||||
# ```
|
||||
|
||||
proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} =
|
||||
{.push checks: off, stackTrace: off.}
|
||||
|
||||
when not defined(nimHasEnforceNoRaises):
|
||||
{.pragma: enforceNoRaises.}
|
||||
|
||||
proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline, enforceNoRaises.} =
|
||||
buf[pos] = digits100[2 * digits]
|
||||
buf[pos+1] = digits100[2 * digits + 1]
|
||||
#copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char)))
|
||||
|
||||
proc trailingZeros2Digits*(digits: uint32): int {.inline.} =
|
||||
proc trailingZeros2Digits*(digits: uint32): int {.inline, enforceNoRaises.} =
|
||||
trailingZeros100[digits]
|
||||
|
||||
when defined(js):
|
||||
proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".}
|
||||
|
||||
func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} =
|
||||
func addChars[T](result: var string, x: T, start: int, n: int) {.inline, enforceNoRaises.} =
|
||||
let old = result.len
|
||||
result.setLen old + n
|
||||
template impl =
|
||||
@@ -52,10 +57,10 @@ func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} =
|
||||
{.noSideEffect.}:
|
||||
copyMem result[old].addr, x[start].unsafeAddr, n
|
||||
|
||||
func addChars[T](result: var string, x: T) {.inline.} =
|
||||
func addChars[T](result: var string, x: T) {.inline, enforceNoRaises.} =
|
||||
addChars(result, x, 0, x.len)
|
||||
|
||||
func addIntImpl(result: var string, x: uint64) {.inline.} =
|
||||
func addIntImpl(result: var string, x: uint64) {.inline, enforceNoRaises.} =
|
||||
var tmp {.noinit.}: array[24, char]
|
||||
var num = x
|
||||
var next = tmp.len - 1
|
||||
@@ -79,8 +84,6 @@ func addIntImpl(result: var string, x: uint64) {.inline.} =
|
||||
dec next
|
||||
addChars(result, tmp, next, tmp.len - next)
|
||||
|
||||
when not defined(nimHasEnforceNoRaises):
|
||||
{.pragma: enforceNoRaises.}
|
||||
|
||||
func addInt*(result: var string, x: uint64) {.enforceNoRaises.} =
|
||||
when nimvm: addIntImpl(result, x)
|
||||
|
||||
@@ -4,7 +4,13 @@ template toLocation*(result: var string, file: string | cstring, line: int, col:
|
||||
## avoids spurious allocations
|
||||
# Hopefully this can be re-used everywhere so that if a user needs to customize,
|
||||
# it can be done in a single place.
|
||||
result.add file
|
||||
when file is cstring:
|
||||
var i = 0
|
||||
while file[i] != '\0':
|
||||
add(result, file[i])
|
||||
inc i
|
||||
else:
|
||||
result.add file
|
||||
if line > 0:
|
||||
result.add "("
|
||||
addInt(result, line)
|
||||
|
||||
@@ -331,7 +331,7 @@ proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
|
||||
if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir)
|
||||
|
||||
proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [
|
||||
WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} =
|
||||
WriteDirEffect, ReadDirEffect], gcsafe, noWeirdTarget.} =
|
||||
## Removes the directory `dir` including all subdirectories and files
|
||||
## in `dir` (recursively).
|
||||
##
|
||||
@@ -441,7 +441,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1",
|
||||
discard existsOrCreateDir(p)
|
||||
|
||||
proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1",
|
||||
tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
|
||||
tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], gcsafe, noWeirdTarget.} =
|
||||
## Copies a directory from `source` to `dest`.
|
||||
##
|
||||
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
|
||||
@@ -482,7 +482,7 @@ proc copyDirWithPermissions*(source, dest: string,
|
||||
ignorePermissionErrors = true,
|
||||
skipSpecial = false)
|
||||
{.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect],
|
||||
benign, noWeirdTarget.} =
|
||||
gcsafe, noWeirdTarget.} =
|
||||
## Copies a directory from `source` to `dest` preserving file permissions.
|
||||
##
|
||||
## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
|
||||
|
||||
@@ -41,7 +41,7 @@ proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.}
|
||||
import std/private/osseps
|
||||
export osseps
|
||||
|
||||
proc absolutePathInternal(path: string): string {.gcsafe.}
|
||||
proc absolutePathInternal(path: string): string {.gcsafe, raises: [ValueError, OSerror].}
|
||||
|
||||
proc normalizePathEnd*(path: var string, trailingSep = false) =
|
||||
## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
|
||||
|
||||
@@ -182,7 +182,7 @@ proc checkErr(f: File) =
|
||||
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
proc readBuffer*(f: File, buffer: pointer, len: Natural): int {.
|
||||
tags: [ReadIOEffect], benign.} =
|
||||
tags: [ReadIOEffect], gcsafe.} =
|
||||
## Reads `len` bytes into the buffer pointed to by `buffer`. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
## `len` (if not as many bytes are remaining), but not greater.
|
||||
@@ -191,20 +191,20 @@ proc readBuffer*(f: File, buffer: pointer, len: Natural): int {.
|
||||
|
||||
proc readBytes*(f: File, a: var openArray[int8|uint8], start,
|
||||
len: Natural): int {.
|
||||
tags: [ReadIOEffect], benign.} =
|
||||
tags: [ReadIOEffect], gcsafe.} =
|
||||
## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
## `len` (if not as many bytes are remaining), but not greater.
|
||||
result = readBuffer(f, addr(a[start]), len)
|
||||
|
||||
proc readChars*(f: File, a: var openArray[char]): int {.tags: [ReadIOEffect], benign.} =
|
||||
proc readChars*(f: File, a: var openArray[char]): int {.tags: [ReadIOEffect], gcsafe.} =
|
||||
## Reads up to `a.len` bytes into the buffer `a`. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
## `a.len` (if not as many bytes are remaining), but not greater.
|
||||
result = readBuffer(f, addr(a[0]), a.len)
|
||||
|
||||
proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {.
|
||||
tags: [ReadIOEffect], benign, deprecated:
|
||||
tags: [ReadIOEffect], gcsafe, deprecated:
|
||||
"use other `readChars` overload, possibly via: readChars(toOpenArray(buf, start, len-1))".} =
|
||||
## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns
|
||||
## the actual number of bytes that have been read which may be less than
|
||||
@@ -213,13 +213,13 @@ proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {.
|
||||
raiseEIO("buffer overflow: (start+len) > length of openarray buffer")
|
||||
result = readBuffer(f, addr(a[start]), len)
|
||||
|
||||
proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
## Writes a value to the file `f`. May throw an IO exception.
|
||||
discard c_fputs(c, f)
|
||||
checkErr(f)
|
||||
|
||||
proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {.
|
||||
tags: [WriteIOEffect], benign.} =
|
||||
tags: [WriteIOEffect], gcsafe.} =
|
||||
## Writes the bytes of buffer pointed to by the parameter `buffer` to the
|
||||
## file `f`. Returns the number of actual written bytes, which may be less
|
||||
## than `len` in case of an error.
|
||||
@@ -227,7 +227,7 @@ proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {.
|
||||
checkErr(f)
|
||||
|
||||
proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {.
|
||||
tags: [WriteIOEffect], benign.} =
|
||||
tags: [WriteIOEffect], gcsafe.} =
|
||||
## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns
|
||||
## the number of actual written bytes, which may be less than `len` in case
|
||||
## of an error.
|
||||
@@ -235,7 +235,7 @@ proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {.
|
||||
result = writeBuffer(f, addr(x[int(start)]), len)
|
||||
|
||||
proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {.
|
||||
tags: [WriteIOEffect], benign.} =
|
||||
tags: [WriteIOEffect], gcsafe.} =
|
||||
## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns
|
||||
## the number of actual written bytes, which may be less than `len` in case
|
||||
## of an error.
|
||||
@@ -264,7 +264,7 @@ when defined(windows):
|
||||
break
|
||||
inc i, w
|
||||
|
||||
proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, s: string) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
when defined(windows):
|
||||
writeWindows(f, s, doRaise = true)
|
||||
else:
|
||||
@@ -393,7 +393,7 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w
|
||||
inheritable.WinDWORD) != 0
|
||||
|
||||
proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
|
||||
benign.} =
|
||||
gcsafe.} =
|
||||
## Reads a line of text from the file `f` into `line`. May throw an IO
|
||||
## exception.
|
||||
## A line of text may be delimited by `LF` or `CRLF`. The newline
|
||||
@@ -519,43 +519,43 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
|
||||
sp = 128 # read in 128 bytes at a time
|
||||
line.setLen(pos+sp)
|
||||
|
||||
proc readLine*(f: File): string {.tags: [ReadIOEffect], benign.} =
|
||||
proc readLine*(f: File): string {.tags: [ReadIOEffect], gcsafe.} =
|
||||
## Reads a line of text from the file `f`. May throw an IO exception.
|
||||
## A line of text may be delimited by `LF` or `CRLF`. The newline
|
||||
## character(s) are not part of the returned string.
|
||||
result = newStringOfCap(80)
|
||||
if not readLine(f, result): raiseEOF()
|
||||
|
||||
proc write*(f: File, i: int) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, i: int) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
when sizeof(int) == 8:
|
||||
if c_fprintf(f, "%lld", i) < 0: checkErr(f)
|
||||
else:
|
||||
if c_fprintf(f, "%ld", i) < 0: checkErr(f)
|
||||
|
||||
proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, i: BiggestInt) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
when sizeof(BiggestInt) == 8:
|
||||
if c_fprintf(f, "%lld", i) < 0: checkErr(f)
|
||||
else:
|
||||
if c_fprintf(f, "%ld", i) < 0: checkErr(f)
|
||||
|
||||
proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, b: bool) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
if b: write(f, "true")
|
||||
else: write(f, "false")
|
||||
|
||||
proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, r: float32) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
var buffer {.noinit.}: array[65, char]
|
||||
discard writeFloatToBuffer(buffer, r)
|
||||
if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
|
||||
|
||||
proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
var buffer {.noinit.}: array[65, char]
|
||||
discard writeFloatToBuffer(buffer, r)
|
||||
if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
|
||||
|
||||
proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, c: char) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
discard c_putc(cint(c), f)
|
||||
|
||||
proc write*(f: File, a: varargs[string, `$`]) {.tags: [WriteIOEffect], benign.} =
|
||||
proc write*(f: File, a: varargs[string, `$`]) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
for x in items(a): write(f, x)
|
||||
|
||||
proc readAllBuffer(file: File): string =
|
||||
@@ -579,7 +579,7 @@ proc rawFileSize(file: File): int64 =
|
||||
result = c_ftell(file)
|
||||
discard c_fseek(file, oldPos, 0)
|
||||
|
||||
proc endOfFile*(f: File): bool {.tags: [], benign.} =
|
||||
proc endOfFile*(f: File): bool {.tags: [], gcsafe.} =
|
||||
## Returns true if `f` is at the end.
|
||||
var c = c_fgetc(f)
|
||||
discard c_ungetc(c, f)
|
||||
@@ -603,7 +603,7 @@ proc readAllFile(file: File): string =
|
||||
var len = rawFileSize(file)
|
||||
result = readAllFile(file, len)
|
||||
|
||||
proc readAll*(file: File): string {.tags: [ReadIOEffect], benign.} =
|
||||
proc readAll*(file: File): string {.tags: [ReadIOEffect], gcsafe.} =
|
||||
## Reads all data from the stream `file`.
|
||||
##
|
||||
## Raises an IO exception in case of an error. It is an error if the
|
||||
@@ -621,7 +621,7 @@ proc readAll*(file: File): string {.tags: [ReadIOEffect], benign.} =
|
||||
result = readAllBuffer(file)
|
||||
|
||||
proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
|
||||
tags: [WriteIOEffect], benign.} =
|
||||
tags: [WriteIOEffect], gcsafe.} =
|
||||
## Writes the values `x` to `f` and then writes "\\n".
|
||||
## May throw an IO exception.
|
||||
for i in items(x):
|
||||
@@ -713,7 +713,7 @@ when defined(posix) and not defined(nimscript):
|
||||
|
||||
proc open*(f: var File, filename: string,
|
||||
mode: FileMode = fmRead,
|
||||
bufSize: int = -1): bool {.tags: [], raises: [], benign.} =
|
||||
bufSize: int = -1): bool {.tags: [], raises: [], gcsafe.} =
|
||||
## Opens a file named `filename` with given `mode`.
|
||||
##
|
||||
## Default mode is readonly. Returns true if the file could be opened.
|
||||
@@ -747,7 +747,7 @@ proc open*(f: var File, filename: string,
|
||||
result = false
|
||||
|
||||
proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
|
||||
tags: [], benign.} =
|
||||
tags: [], gcsafe.} =
|
||||
## Reopens the file `f` with given `filename` and `mode`. This
|
||||
## is often used to redirect the `stdin`, `stdout` or `stderr`
|
||||
## file variables.
|
||||
@@ -766,7 +766,7 @@ proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {.
|
||||
result = false
|
||||
|
||||
proc open*(f: var File, filehandle: FileHandle,
|
||||
mode: FileMode = fmRead): bool {.tags: [], raises: [], benign.} =
|
||||
mode: FileMode = fmRead): bool {.tags: [], raises: [], gcsafe.} =
|
||||
## Creates a `File` from a `filehandle` with given `mode`.
|
||||
##
|
||||
## Default mode is readonly. Returns true if the file could be opened.
|
||||
@@ -792,26 +792,26 @@ proc open*(filename: string,
|
||||
if not open(result, filename, mode, bufSize):
|
||||
raise newException(IOError, "cannot open: " & filename)
|
||||
|
||||
proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign, sideEffect.} =
|
||||
proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.gcsafe, sideEffect.} =
|
||||
## Sets the position of the file pointer that is used for read/write
|
||||
## operations. The file's first byte has the index zero.
|
||||
if c_fseek(f, pos, cint(relativeTo)) != 0:
|
||||
raiseEIO("cannot set file position")
|
||||
|
||||
proc getFilePos*(f: File): int64 {.benign.} =
|
||||
proc getFilePos*(f: File): int64 {.gcsafe.} =
|
||||
## Retrieves the current position of the file pointer that is used to
|
||||
## read from the file `f`. The file's first byte has the index zero.
|
||||
result = c_ftell(f)
|
||||
if result < 0: raiseEIO("cannot retrieve file position")
|
||||
|
||||
proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} =
|
||||
proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], gcsafe.} =
|
||||
## Retrieves the file size (in bytes) of `f`.
|
||||
let oldPos = getFilePos(f)
|
||||
discard c_fseek(f, 0, 2) # seek the end of the file
|
||||
result = getFilePos(f)
|
||||
setFilePos(f, oldPos)
|
||||
|
||||
proc setStdIoUnbuffered*() {.tags: [], benign.} =
|
||||
proc setStdIoUnbuffered*() {.tags: [], gcsafe.} =
|
||||
## Configures `stdin`, `stdout` and `stderr` to be unbuffered.
|
||||
when declared(stdout):
|
||||
discard c_setvbuf(stdout, nil, IONBF, 0)
|
||||
@@ -865,7 +865,7 @@ when defined(windows) and appType == "console" and
|
||||
discard setConsoleCP(Utf8codepage)
|
||||
addExitProc(restoreConsoleCP)
|
||||
|
||||
proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} =
|
||||
proc readFile*(filename: string): string {.tags: [ReadIOEffect], gcsafe.} =
|
||||
## Opens a file named `filename` for reading, calls `readAll
|
||||
## <#readAll,File>`_ and closes the file afterwards. Returns the string.
|
||||
## Raises an IO exception in case of an error. If you need to call
|
||||
@@ -880,7 +880,7 @@ proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} =
|
||||
else:
|
||||
raise newException(IOError, "cannot open: " & filename)
|
||||
|
||||
proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} =
|
||||
proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], gcsafe.} =
|
||||
## Opens a file named `filename` for writing. Then writes the
|
||||
## `content` completely to the file and closes the file afterwards.
|
||||
## Raises an IO exception in case of an error.
|
||||
|
||||
@@ -230,11 +230,11 @@ elif someVcc:
|
||||
proc atomicCompareExchangeN*[T: ptr](p, expected: ptr T, desired: T,
|
||||
weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool =
|
||||
when sizeof(T) == 8:
|
||||
interlockedCompareExchange64(p, cast[int64](desired), cast[int64](expected)) ==
|
||||
cast[int64](expected)
|
||||
interlockedCompareExchange64(p, cast[int64](desired), cast[int64](expected[])) ==
|
||||
cast[int64](expected[])
|
||||
elif sizeof(T) == 4:
|
||||
interlockedCompareExchange32(p, cast[int32](desired), cast[int32](expected)) ==
|
||||
cast[int32](expected)
|
||||
interlockedCompareExchange32(p, cast[int32](desired), cast[int32](expected[])) ==
|
||||
cast[int32](expected[])
|
||||
|
||||
proc atomicExchangeN*[T: ptr](p: ptr T, val: T, mem: AtomMemModel): T =
|
||||
when sizeof(T) == 8:
|
||||
|
||||
343
lib/system.nim
343
lib/system.nim
@@ -955,6 +955,22 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {.
|
||||
## assert x == @[10]
|
||||
## ```
|
||||
|
||||
when defined(nimHasSetLengthSeqUninitMagic):
|
||||
func setLenUninit*[T](s: var seq[T], newlen: Natural) {.magic: "SetLengthSeqUninit", nodestroy.} =
|
||||
## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
|
||||
## New slots will not be initialized.
|
||||
##
|
||||
## If the current length is greater than the new length,
|
||||
## `s` will be truncated.
|
||||
## ```nim
|
||||
## var x = @[10, 20]
|
||||
## x.setLenUninit(5)
|
||||
## x[4] = 50
|
||||
## assert x[4] == 50Add commentMore actions
|
||||
## x.setLenUninit(1)
|
||||
## assert x == @[10]
|
||||
## ```
|
||||
|
||||
proc setLen*(s: var string, newlen: Natural) {.
|
||||
magic: "SetLengthStr", noSideEffect.}
|
||||
## Sets the length of string `s` to `newlen`.
|
||||
@@ -1134,7 +1150,7 @@ template sysAssert(cond: bool, msg: string) =
|
||||
const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
|
||||
|
||||
when notJSnotNims and hasAlloc and not defined(nimSeqsV2):
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerproc, gcsafe.}
|
||||
|
||||
when defined(nimscript) or not defined(nimSeqsV2):
|
||||
proc add*[T](x: var seq[T], y: sink T) {.magic: "AppendSeqElem", noSideEffect.}
|
||||
@@ -1605,7 +1621,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
|
||||
|
||||
when notJSnotNims:
|
||||
import system/ansi_c
|
||||
import system/memory
|
||||
include system/sysmem
|
||||
|
||||
|
||||
{.push stackTrace: off.}
|
||||
@@ -1616,7 +1632,7 @@ when not defined(js) and hasThreadSupport and hostOS != "standalone":
|
||||
|
||||
when not defined(js) and defined(nimV2):
|
||||
type
|
||||
DestructorProc = proc (p: pointer) {.nimcall, benign, raises: [].}
|
||||
DestructorProc = proc (p: pointer) {.nimcall, gcsafe, raises: [].}
|
||||
TNimTypeV2 {.compilerproc.} = object
|
||||
destructor: pointer
|
||||
size: int
|
||||
@@ -1724,6 +1740,28 @@ when not defined(nimscript):
|
||||
when not declared(sysFatal):
|
||||
include "system/fatal"
|
||||
|
||||
proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", gcsafe, sideEffect.}
|
||||
## Writes and flushes the parameters to the standard output.
|
||||
##
|
||||
## Special built-in that takes a variable number of arguments. Each argument
|
||||
## is converted to a string via `$`, so it works for user-defined
|
||||
## types that have an overloaded `$` operator.
|
||||
## It is roughly equivalent to `writeLine(stdout, x); flushFile(stdout)`, but
|
||||
## available for the JavaScript target too.
|
||||
##
|
||||
## Unlike other IO operations this is guaranteed to be thread-safe as
|
||||
## `echo` is very often used for debugging convenience. If you want to use
|
||||
## `echo` inside a `proc without side effects
|
||||
## <manual.html#pragmas-nosideeffect-pragma>`_ you can use `debugEcho
|
||||
## <#debugEcho,varargs[typed,]>`_ instead.
|
||||
|
||||
proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
## Same as `echo <#echo,varargs[typed,]>`_, but as a special semantic rule,
|
||||
## `debugEcho` pretends to be free of side effects, so that it can be used
|
||||
## for debugging routines marked as `noSideEffect
|
||||
## <manual.html#pragmas-nosideeffect-pragma>`_.
|
||||
|
||||
type
|
||||
PFrame* = ptr TFrame ## Represents a runtime frame of the call stack;
|
||||
## part of the debugger API.
|
||||
@@ -1738,6 +1776,15 @@ type
|
||||
when NimStackTraceMsgs:
|
||||
frameMsgLen*: int ## end position in frameMsgBuf for this frame.
|
||||
|
||||
when notJSnotNims and not gotoBasedExceptions:
|
||||
type
|
||||
PSafePoint = ptr TSafePoint
|
||||
TSafePoint {.compilerproc, final.} = object
|
||||
prev: PSafePoint # points to next safe point ON THE STACK
|
||||
status: int
|
||||
context: C_JmpBuf
|
||||
SafePoint = TSafePoint
|
||||
|
||||
when defined(nimV2):
|
||||
var
|
||||
framePtr {.threadvar.}: PFrame
|
||||
@@ -1750,6 +1797,113 @@ template newException*(exceptn: typedesc, message: string;
|
||||
## to `message`. Returns the new exception object.
|
||||
(ref exceptn)(msg: message, parent: parentException)
|
||||
|
||||
# we have to compute this here before turning it off in except.nim anyway ...
|
||||
const NimStackTrace = compileOption("stacktrace")
|
||||
const
|
||||
usesDestructors = defined(gcDestructors) or defined(gcHooks)
|
||||
|
||||
include "system/gc_interface"
|
||||
|
||||
when notJSnotNims:
|
||||
proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.}
|
||||
## Allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
##
|
||||
## The handler runs inside a C signal handler and comes with similar
|
||||
## limitations.
|
||||
##
|
||||
## Allocating memory and interacting with most system calls, including using
|
||||
## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined
|
||||
## behavior and will likely lead to application crashes.
|
||||
##
|
||||
## The OS may call the ctrl-c handler from any thread, including threads
|
||||
## that were not created by Nim, such as happens on Windows.
|
||||
##
|
||||
## ## Example:
|
||||
##
|
||||
## ```nim
|
||||
## var stop: Atomic[bool]
|
||||
## proc ctrlc() {.noconv.} =
|
||||
## # Using atomics types is safe!
|
||||
## stop.store(true)
|
||||
##
|
||||
## setControlCHook(ctrlc)
|
||||
##
|
||||
## while not stop.load():
|
||||
## echo "Still running.."
|
||||
## sleep(1000)
|
||||
## ```
|
||||
|
||||
when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||
proc unsetControlCHook*()
|
||||
## Reverts a call to setControlCHook.
|
||||
|
||||
when hostOS != "standalone":
|
||||
proc getStackTrace*(): string {.gcsafe.}
|
||||
## Gets the current stack trace. This only works for debug builds.
|
||||
|
||||
proc getStackTrace*(e: ref Exception): string {.gcsafe.}
|
||||
## Gets the stack trace associated with `e`, which is the stack that
|
||||
## lead to the `raise` statement. This only works for debug builds.
|
||||
|
||||
var
|
||||
globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, gcsafe.}
|
||||
## With this hook you can influence exception handling on a global level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `globalRaiseHook` returns false, the exception is caught and does
|
||||
## not propagate further through the call stack.
|
||||
|
||||
localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, gcsafe.}
|
||||
## With this hook you can influence exception handling on a
|
||||
## thread local level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `localRaiseHook` returns false, the exception
|
||||
## is caught and does not propagate further through the call stack.
|
||||
|
||||
outOfMemHook*: proc () {.nimcall, tags: [], gcsafe, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `out of memory`:idx: event. The standard handler
|
||||
## writes an error message and terminates the program.
|
||||
##
|
||||
## `outOfMemHook` can be used to raise an exception in case of OOM like so:
|
||||
##
|
||||
## ```nim
|
||||
## var gOutOfMem: ref EOutOfMemory
|
||||
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
|
||||
## gOutOfMem.msg = "out of memory"
|
||||
##
|
||||
## proc handleOOM() =
|
||||
## raise gOutOfMem
|
||||
##
|
||||
## system.outOfMemHook = handleOOM
|
||||
## ```
|
||||
##
|
||||
## If the handler does not raise an exception, ordinary control flow
|
||||
## continues and the program is terminated.
|
||||
|
||||
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], gcsafe, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `unhandle exception` event. The standard handler
|
||||
## writes an error message and terminates the program, except when
|
||||
## using `--os:any`
|
||||
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
when defined(memtracker):
|
||||
include "system/memtracker"
|
||||
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
include "system/excpt"
|
||||
{.pop.}
|
||||
|
||||
|
||||
when not defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
export assertions
|
||||
@@ -1826,11 +1980,6 @@ proc `<`*[T: tuple](x, y: T): bool =
|
||||
return false
|
||||
|
||||
|
||||
include "system/gc_interface"
|
||||
|
||||
# we have to compute this here before turning it off in except.nim anyway ...
|
||||
const NimStackTrace = compileOption("stacktrace")
|
||||
|
||||
import system/coro_detection
|
||||
|
||||
{.push checks: off.}
|
||||
@@ -1839,53 +1988,6 @@ import system/coro_detection
|
||||
# however, stack-traces are available for most parts
|
||||
# of the code
|
||||
|
||||
when notJSnotNims:
|
||||
var
|
||||
globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.}
|
||||
## With this hook you can influence exception handling on a global level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `globalRaiseHook` returns false, the exception is caught and does
|
||||
## not propagate further through the call stack.
|
||||
|
||||
localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.}
|
||||
## With this hook you can influence exception handling on a
|
||||
## thread local level.
|
||||
## If not nil, every 'raise' statement ends up calling this hook.
|
||||
##
|
||||
## .. warning:: Ordinary application code should never set this hook! You better know what you do when setting this.
|
||||
##
|
||||
## If `localRaiseHook` returns false, the exception
|
||||
## is caught and does not propagate further through the call stack.
|
||||
|
||||
outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `out of memory`:idx: event. The standard handler
|
||||
## writes an error message and terminates the program.
|
||||
##
|
||||
## `outOfMemHook` can be used to raise an exception in case of OOM like so:
|
||||
##
|
||||
## ```nim
|
||||
## var gOutOfMem: ref EOutOfMemory
|
||||
## new(gOutOfMem) # need to be allocated *before* OOM really happened!
|
||||
## gOutOfMem.msg = "out of memory"
|
||||
##
|
||||
## proc handleOOM() =
|
||||
## raise gOutOfMem
|
||||
##
|
||||
## system.outOfMemHook = handleOOM
|
||||
## ```
|
||||
##
|
||||
## If the handler does not raise an exception, ordinary control flow
|
||||
## continues and the program is terminated.
|
||||
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
|
||||
## Set this variable to provide a procedure that should be called
|
||||
## in case of an `unhandle exception` event. The standard handler
|
||||
## writes an error message and terminates the program, except when
|
||||
## using `--os:any`
|
||||
|
||||
when defined(js) or defined(nimdoc):
|
||||
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
|
||||
## Appends `y` to `x` in place.
|
||||
@@ -1922,34 +2024,13 @@ elif hasAlloc:
|
||||
inc(i)
|
||||
{.pop.}
|
||||
|
||||
proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", benign, sideEffect.}
|
||||
## Writes and flushes the parameters to the standard output.
|
||||
##
|
||||
## Special built-in that takes a variable number of arguments. Each argument
|
||||
## is converted to a string via `$`, so it works for user-defined
|
||||
## types that have an overloaded `$` operator.
|
||||
## It is roughly equivalent to `writeLine(stdout, x); flushFile(stdout)`, but
|
||||
## available for the JavaScript target too.
|
||||
##
|
||||
## Unlike other IO operations this is guaranteed to be thread-safe as
|
||||
## `echo` is very often used for debugging convenience. If you want to use
|
||||
## `echo` inside a `proc without side effects
|
||||
## <manual.html#pragmas-nosideeffect-pragma>`_ you can use `debugEcho
|
||||
## <#debugEcho,varargs[typed,]>`_ instead.
|
||||
|
||||
proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
## Same as `echo <#echo,varargs[typed,]>`_, but as a special semantic rule,
|
||||
## `debugEcho` pretends to be free of side effects, so that it can be used
|
||||
## for debugging routines marked as `noSideEffect
|
||||
## <manual.html#pragmas-nosideeffect-pragma>`_.
|
||||
|
||||
when hostOS == "standalone" and defined(nogc):
|
||||
proc nimToCStringConv(s: NimString): cstring {.compilerproc, inline.} =
|
||||
if s == nil or s.len == 0: result = cstring""
|
||||
else: result = cast[cstring](addr s.data)
|
||||
|
||||
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
|
||||
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.}
|
||||
## Get type information for `x`.
|
||||
##
|
||||
## Ordinary code should not use this, but the `typeinfo module
|
||||
@@ -2012,6 +2093,16 @@ template unlikely*(val: bool): bool =
|
||||
import system/dollars
|
||||
export dollars
|
||||
|
||||
when notJSnotNims:
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
|
||||
include "system/chcks"
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/integerops"
|
||||
{.pop.}
|
||||
|
||||
when defined(nimAuditDelete):
|
||||
{.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".}
|
||||
else:
|
||||
@@ -2094,17 +2185,17 @@ when notJSnotNims:
|
||||
nimZeroMem(p, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("zeroMem", p, size)
|
||||
proc copyMem(dest, source: pointer, size: Natural) =
|
||||
proc copyMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} =
|
||||
nimCopyMem(dest, source, size)
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("copyMem", dest, size)
|
||||
proc moveMem(dest, source: pointer, size: Natural) =
|
||||
proc moveMem(dest, source: pointer, size: Natural) {.enforceNoRaises.} =
|
||||
c_memmove(dest, source, csize_t(size))
|
||||
when declared(memTrackerOp):
|
||||
memTrackerOp("moveMem", dest, size)
|
||||
proc equalMem(a, b: pointer, size: Natural): bool =
|
||||
proc equalMem(a, b: pointer, size: Natural): bool {.enforceNoRaises.} =
|
||||
nimCmpMem(a, b, size) == 0
|
||||
proc cmpMem(a, b: pointer, size: Natural): int =
|
||||
proc cmpMem(a, b: pointer, size: Natural): int {.enforceNoRaises.} =
|
||||
nimCmpMem(a, b, size).int
|
||||
|
||||
when not defined(js) or defined(nimscript):
|
||||
@@ -2157,15 +2248,6 @@ when not defined(js) and declared(alloc0) and declared(dealloc):
|
||||
inc(i)
|
||||
dealloc(a)
|
||||
|
||||
when notJSnotNims and not gotoBasedExceptions:
|
||||
type
|
||||
PSafePoint = ptr TSafePoint
|
||||
TSafePoint {.compilerproc, final.} = object
|
||||
prev: PSafePoint # points to next safe point ON THE STACK
|
||||
status: int
|
||||
context: C_JmpBuf
|
||||
SafePoint = TSafePoint
|
||||
|
||||
when not defined(js):
|
||||
when hasThreadSupport:
|
||||
when hostOS != "standalone":
|
||||
@@ -2178,63 +2260,6 @@ when not defined(js):
|
||||
when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
|
||||
when declared(initGC): initGC()
|
||||
|
||||
when notJSnotNims:
|
||||
proc setControlCHook*(hook: proc () {.noconv.}) {.raises: [], gcsafe.}
|
||||
## Allows you to override the behaviour of your application when CTRL+C
|
||||
## is pressed. Only one such hook is supported.
|
||||
##
|
||||
## The handler runs inside a C signal handler and comes with similar
|
||||
## limitations.
|
||||
##
|
||||
## Allocating memory and interacting with most system calls, including using
|
||||
## `echo`, `string`, `seq`, raising or catching exceptions etc is undefined
|
||||
## behavior and will likely lead to application crashes.
|
||||
##
|
||||
## The OS may call the ctrl-c handler from any thread, including threads
|
||||
## that were not created by Nim, such as happens on Windows.
|
||||
##
|
||||
## ## Example:
|
||||
##
|
||||
## ```nim
|
||||
## var stop: Atomic[bool]
|
||||
## proc ctrlc() {.noconv.} =
|
||||
## # Using atomics types is safe!
|
||||
## stop.store(true)
|
||||
##
|
||||
## setControlCHook(ctrlc)
|
||||
##
|
||||
## while not stop.load():
|
||||
## echo "Still running.."
|
||||
## sleep(1000)
|
||||
## ```
|
||||
|
||||
when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||
proc unsetControlCHook*()
|
||||
## Reverts a call to setControlCHook.
|
||||
|
||||
when hostOS != "standalone":
|
||||
proc getStackTrace*(): string {.gcsafe.}
|
||||
## Gets the current stack trace. This only works for debug builds.
|
||||
|
||||
proc getStackTrace*(e: ref Exception): string {.gcsafe.}
|
||||
## Gets the stack trace associated with `e`, which is the stack that
|
||||
## lead to the `raise` statement. This only works for debug builds.
|
||||
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
when defined(memtracker):
|
||||
include "system/memtracker"
|
||||
|
||||
when hostOS == "standalone":
|
||||
include "system/embedded"
|
||||
else:
|
||||
include "system/excpt"
|
||||
include "system/chcks"
|
||||
|
||||
# we cannot compile this with stack tracing on
|
||||
# as it would recurse endlessly!
|
||||
include "system/integerops"
|
||||
{.pop.}
|
||||
|
||||
|
||||
when not defined(js):
|
||||
# this is a hack: without this when statement, you would get:
|
||||
@@ -3091,3 +3116,27 @@ proc arrayWithDefault*[T](size: static int): array[size, T] {.noinit, nodestroy,
|
||||
## Creates a new array filled with `default(T)`.
|
||||
for i in 0..size-1:
|
||||
result[i] = default(T)
|
||||
|
||||
when hostOS == "standalone":
|
||||
# Include panicoverride.nim late so users can use the full extent of the
|
||||
# language in their custom panic handlers (e.g. macros).
|
||||
# Users define `proc panic(msg: string)` and `proc rawoutput(msg: string)`.
|
||||
include "$projectpath/panicoverride"
|
||||
|
||||
when not declared(panic):
|
||||
{.error:
|
||||
"a panic proc with the following signature must be provided " &
|
||||
"when compiling with --os:standalone: " &
|
||||
"`proc panic(msg: string) {.nimcall.}`".}
|
||||
|
||||
when not declared(rawoutput):
|
||||
{.error:
|
||||
"a rawoutput proc with the following signature must be provided " &
|
||||
"when compiling with --os:standalone: " &
|
||||
"`proc rawoutput(msg: string) {.nimcall.}`".}
|
||||
|
||||
# Wrappers with exportc that fatal.nim references via importc.
|
||||
# This way panicoverride keeps old API and can still be included without
|
||||
# ssymbols being duplicated.
|
||||
proc nimPanic(s: string) {.exportc, noreturn.} = panic(s)
|
||||
proc nimRawoutput(s: string) {.exportc.} = rawoutput(s)
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
{.push profiler:off.}
|
||||
|
||||
include osalloc
|
||||
import std/private/syslocks
|
||||
|
||||
template track(op, address, size) =
|
||||
when defined(memTracker):
|
||||
@@ -478,7 +477,8 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} =
|
||||
a = a +% size
|
||||
else:
|
||||
let c = cast[PBigChunk](c)
|
||||
yield addr(c.data)
|
||||
# prev stores the aligned data pointer set during rawAlloc
|
||||
yield cast[pointer](c.prev)
|
||||
m.locked = false
|
||||
|
||||
proc iterToProc*(iter: typed, envType: typedesc; procName: untyped) {.
|
||||
@@ -725,7 +725,7 @@ proc getSmallChunk(a: var MemRegion): PSmallChunk =
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
when not defined(gcDestructors):
|
||||
proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.benign.}
|
||||
proc isAllocatedPtr(a: MemRegion, p: pointer): bool {.gcsafe.}
|
||||
|
||||
when true:
|
||||
template allocInv(a: MemRegion): bool = true
|
||||
@@ -778,7 +778,10 @@ proc deallocBigChunk(a: var MemRegion, c: PBigChunk) =
|
||||
sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
|
||||
when not defined(gcDestructors):
|
||||
a.deleted = getBottom(a)
|
||||
del(a, a.root, cast[int](addr(c.data)))
|
||||
# prev stores the aligned data pointer that was added to the AVL tree during allocation
|
||||
del(a, a.root, cast[int](c.prev))
|
||||
# Reset prev before freeing (required by listAdd assertions in freeBigChunk)
|
||||
c.prev = nil
|
||||
if c.size >= HugeChunkSize: freeHugeChunk(a, c)
|
||||
else: freeBigChunk(a, c)
|
||||
|
||||
@@ -837,7 +840,23 @@ when defined(gcDestructors):
|
||||
dec maxIters
|
||||
if it == nil: break
|
||||
|
||||
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
when defined(heaptrack):
|
||||
const heaptrackLib =
|
||||
when defined(heaptrack_inject):
|
||||
"libheaptrack_inject.so"
|
||||
else:
|
||||
"libheaptrack_preload.so"
|
||||
proc heaptrack_malloc(a: pointer, size: int) {.cdecl, importc, dynlib: heaptrackLib.}
|
||||
proc heaptrack_free(a: pointer) {.cdecl, importc, dynlib: heaptrackLib.}
|
||||
|
||||
proc bigChunkAlignOffset(alignment: int): int {.inline.} =
|
||||
## Compute the alignment offset for big chunk data.
|
||||
if alignment == 0:
|
||||
result = 0
|
||||
else:
|
||||
result = align(sizeof(BigChunk) + sizeof(Cell), alignment) - sizeof(BigChunk) - sizeof(Cell)
|
||||
|
||||
proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int = 0): pointer =
|
||||
when defined(nimTypeNames):
|
||||
inc(a.allocCounter)
|
||||
sysAssert(allocInv(a), "rawAlloc: begin")
|
||||
@@ -847,7 +866,9 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
sysAssert(size >= requestedSize, "insufficient allocated size!")
|
||||
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
|
||||
|
||||
if size <= SmallChunkSize-smallChunkOverhead():
|
||||
# For custom alignments > MemAlign, force big chunk allocation
|
||||
# Small chunks cannot handle arbitrary alignments due to fixed cell boundaries
|
||||
if size <= SmallChunkSize-smallChunkOverhead() and alignment == 0:
|
||||
template fetchSharedCells(tc: PSmallChunk) =
|
||||
# Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]`
|
||||
when defined(gcDestructors):
|
||||
@@ -942,13 +963,21 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
if deferredFrees != nil:
|
||||
freeDeferredObjects(a, deferredFrees)
|
||||
|
||||
size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize)
|
||||
# For big chunks with custom alignment, allocate extra space.
|
||||
# Since chunks are page-aligned, the needed padding is a compile-time
|
||||
# deterministic value rather than a worst-case estimate.
|
||||
let alignPad = bigChunkAlignOffset(alignment)
|
||||
size = requestedSize + bigChunkOverhead() + alignPad
|
||||
# allocate a large block
|
||||
var c = if size >= HugeChunkSize: getHugeChunk(a, size)
|
||||
else: getBigChunk(a, size)
|
||||
sysAssert c.prev == nil, "rawAlloc 10"
|
||||
sysAssert c.next == nil, "rawAlloc 11"
|
||||
result = addr(c.data)
|
||||
result = addr(c.data) +! alignPad
|
||||
# Store the aligned data pointer in prev for deallocation and GC traversal.
|
||||
# prev is unused while the chunk is allocated (next/prev are free-list links).
|
||||
c.prev = cast[PBigChunk](result)
|
||||
|
||||
sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13")
|
||||
sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
|
||||
when not defined(gcDestructors):
|
||||
@@ -959,6 +988,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
|
||||
sysAssert(isAccessible(a, result), "rawAlloc 14")
|
||||
sysAssert(allocInv(a), "rawAlloc: end")
|
||||
when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a)
|
||||
when defined(heaptrack):
|
||||
heaptrack_malloc(result, requestedSize)
|
||||
|
||||
proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
|
||||
result = rawAlloc(a, requestedSize)
|
||||
@@ -967,6 +998,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
|
||||
proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
when defined(nimTypeNames):
|
||||
inc(a.deallocCounter)
|
||||
when defined(heaptrack):
|
||||
heaptrack_free(p)
|
||||
#sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
|
||||
sysAssert(allocInv(a), "rawDealloc: begin")
|
||||
var c = pageAddr(p)
|
||||
@@ -1013,13 +1046,29 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
inc(c.free, s)
|
||||
else:
|
||||
inc(c.free, s)
|
||||
# Free only if the entire chunk is unused and there are no borrowed cells.
|
||||
# If the chunk were to be freed while it references foreign cells,
|
||||
# the foreign chunks will leak memory and can never be freed.
|
||||
if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
|
||||
listRemove(a.freeSmallChunks[s div MemAlign], c)
|
||||
c.size = SmallChunkSize
|
||||
freeBigChunk(a, cast[PBigChunk](c))
|
||||
# FIX: Don't free small chunks to avoid race condition with sharedFreeLists.
|
||||
#
|
||||
# RACE CONDITION: Between checking foreignCells==0 and calling freeBigChunk,
|
||||
# another thread may read chunk.owner and decide to add a cell to our
|
||||
# sharedFreeLists. If we free the chunk, that cell becomes orphaned.
|
||||
#
|
||||
# SOLUTION: Never free small chunks. They remain in freeSmallChunks[s] and
|
||||
# are reused on next allocation. This maintains the invariant that chunks
|
||||
# in freeSmallChunks[s] have c.free >= s (completely free chunks satisfy this).
|
||||
# If a chunk becomes exhausted (c.free < s), it's removed by line 949.
|
||||
#
|
||||
# TRADEOFF: Memory not returned to OS. Bounded by peak concurrent allocation
|
||||
# per size class (~4KB per active size class per thread, typically <1MB total).
|
||||
#
|
||||
# VERIFIED: TLA+ formal proof shows no race - see VERIFICATION_RESULTS.md
|
||||
#
|
||||
# Original code (REMOVED to fix race):
|
||||
sysAssert(c.free >= s, "Invariant violated: chunk in freeSmallChunks has insufficient space")
|
||||
when false:
|
||||
if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
|
||||
listRemove(a.freeSmallChunks[s div MemAlign], c)
|
||||
c.size = SmallChunkSize
|
||||
freeBigChunk(a, cast[PBigChunk](c))
|
||||
else:
|
||||
when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))
|
||||
|
||||
@@ -1055,7 +1104,9 @@ when not defined(gcDestructors):
|
||||
(cast[ptr FreeCell](p).zeroField >% 1)
|
||||
else:
|
||||
var c = cast[PBigChunk](c)
|
||||
result = p == addr(c.data) and cast[ptr FreeCell](p).zeroField >% 1
|
||||
# prev stores the aligned data pointer set during rawAlloc
|
||||
let cellPtr = cast[pointer](c.prev)
|
||||
result = p == cellPtr and cast[ptr FreeCell](p).zeroField >% 1
|
||||
|
||||
proc prepareForInteriorPointerChecking(a: var MemRegion) {.inline.} =
|
||||
a.minLargeObj = lowGauge(a.root)
|
||||
@@ -1079,7 +1130,8 @@ when not defined(gcDestructors):
|
||||
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
|
||||
else:
|
||||
var c = cast[PBigChunk](c)
|
||||
var d = addr(c.data)
|
||||
# prev stores the aligned data pointer set during rawAlloc
|
||||
var d = cast[pointer](c.prev)
|
||||
if p >= d and cast[ptr FreeCell](d).zeroField >% 1:
|
||||
result = d
|
||||
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
|
||||
@@ -1092,7 +1144,8 @@ when not defined(gcDestructors):
|
||||
if avlNode != nil:
|
||||
var k = cast[pointer](avlNode.key)
|
||||
var c = cast[PBigChunk](pageAddr(k))
|
||||
sysAssert(addr(c.data) == k, " k is not the same as addr(c.data)!")
|
||||
# prev stores the aligned data pointer (the AVL tree key)
|
||||
sysAssert(cast[pointer](c.prev) == k, " k is not the aligned address!")
|
||||
if cast[ptr FreeCell](k).zeroField >% 1:
|
||||
result = k
|
||||
sysAssert isAllocatedPtr(a, result), " result wrong pointer!"
|
||||
@@ -1311,4 +1364,4 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
|
||||
#sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
|
||||
{.pop.}
|
||||
|
||||
{.pop.}
|
||||
{.pop.}
|
||||
@@ -1,4 +1,4 @@
|
||||
proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} =
|
||||
proc succ*[T: Ordinal, V: SomeInteger](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} =
|
||||
## Returns the `y`-th successor (default: 1) of the value `x`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised
|
||||
@@ -7,7 +7,7 @@ proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} =
|
||||
assert succ(5) == 6
|
||||
assert succ(5, 3) == 8
|
||||
|
||||
proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} =
|
||||
proc pred*[T: Ordinal, V: SomeInteger](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} =
|
||||
## Returns the `y`-th predecessor (default: 1) of the value `x`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised
|
||||
@@ -16,7 +16,7 @@ proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} =
|
||||
assert pred(5) == 4
|
||||
assert pred(5, 3) == 2
|
||||
|
||||
proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} =
|
||||
proc inc*[T: Ordinal, V: SomeInteger](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} =
|
||||
## Increments the ordinal `x` by `y`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised or a compile
|
||||
@@ -28,7 +28,7 @@ proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} =
|
||||
inc(i, 3)
|
||||
assert i == 6
|
||||
|
||||
proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} =
|
||||
proc dec*[T: Ordinal, V: SomeInteger](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} =
|
||||
## Decrements the ordinal `x` by `y`.
|
||||
##
|
||||
## If such a value does not exist, `OverflowDefect` is raised or a compile
|
||||
@@ -134,7 +134,10 @@ when defined(nimOldShiftRight):
|
||||
else:
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
|
||||
## Computes the `shift right` operation of `x` and `y`, filling
|
||||
## vacant bit positions with the sign bit.
|
||||
## vacant bit positions with the sign bit. `y` (the number of
|
||||
## positions to shift) is reduced to modulo `sizeof(x) * 8`.
|
||||
## That is `15'i32 shr 35` is equivalent to `15'i32 shr 3`
|
||||
## bitmasked to always be in the range `0 ..< sizeof(int)`.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
@@ -156,7 +159,9 @@ else:
|
||||
|
||||
|
||||
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.} =
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
## Computes the `shift left` operation of `x` and `y`. `y` (the number of
|
||||
## positions to shift) is reduced to modulo `sizeof(x) * 8`.
|
||||
## That is `15'i32 shl 35` is equivalent to `15'i32 shl 3`.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
@@ -170,7 +175,9 @@ proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
|
||||
## Shifts right by pushing copies of the leftmost bit in from the left,
|
||||
## and let the rightmost bits fall off.
|
||||
## and let the rightmost bits fall off. `y` (the number of
|
||||
## positions to shift) is reduced to modulo `sizeof(x) * 8`.
|
||||
## That is `ashr(15'i32, 35)` is equivalent to `ashr(15'i32, 3)`.
|
||||
##
|
||||
## Note that `ashr` is not an operator so use the normal function
|
||||
## call syntax for it.
|
||||
@@ -179,7 +186,7 @@ proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
|
||||
## * `shr func<#shr,int,SomeInteger>`_
|
||||
runnableExamples:
|
||||
assert ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8
|
||||
assert ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8
|
||||
assert ashr(0b1000_0000'i8, 8) == 0b1000_0000'i8
|
||||
assert ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8
|
||||
proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
|
||||
proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
include seqs_v2_reimpl
|
||||
|
||||
proc genericResetAux(dest: pointer, n: ptr TNimNode) {.benign.}
|
||||
proc genericResetAux(dest: pointer, n: ptr TNimNode) {.gcsafe.}
|
||||
|
||||
proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign.}
|
||||
proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.gcsafe.}
|
||||
proc genericAssignAux(dest, src: pointer, n: ptr TNimNode,
|
||||
shallow: bool) {.benign.} =
|
||||
shallow: bool) {.gcsafe.} =
|
||||
var
|
||||
d = cast[int](dest)
|
||||
s = cast[int](src)
|
||||
@@ -187,8 +187,8 @@ proc genericAssignOpenArray(dest, src: pointer, len: int,
|
||||
genericAssign(cast[pointer](d +% i *% mt.base.size),
|
||||
cast[pointer](s +% i *% mt.base.size), mt.base)
|
||||
|
||||
proc objectInit(dest: pointer, typ: PNimType) {.compilerproc, benign.}
|
||||
proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} =
|
||||
proc objectInit(dest: pointer, typ: PNimType) {.compilerproc, gcsafe.}
|
||||
proc objectInitAux(dest: pointer, n: ptr TNimNode) {.gcsafe.} =
|
||||
var d = cast[int](dest)
|
||||
case n.kind
|
||||
of nkNone: sysAssert(false, "objectInitAux")
|
||||
@@ -224,7 +224,7 @@ proc objectInit(dest: pointer, typ: PNimType) =
|
||||
|
||||
# ---------------------- assign zero -----------------------------------------
|
||||
|
||||
proc genericReset(dest: pointer, mt: PNimType) {.compilerproc, benign.}
|
||||
proc genericReset(dest: pointer, mt: PNimType) {.compilerproc, gcsafe.}
|
||||
proc genericResetAux(dest: pointer, n: ptr TNimNode) =
|
||||
var d = cast[int](dest)
|
||||
case n.kind
|
||||
|
||||
@@ -51,7 +51,7 @@ proc split(t: var PAvlNode) =
|
||||
t.link[0] = temp
|
||||
inc t.level
|
||||
|
||||
proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
|
||||
proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.gcsafe.} =
|
||||
if t.isBottom:
|
||||
t = allocAvlNode(a, key, upperBound)
|
||||
else:
|
||||
@@ -70,7 +70,7 @@ proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} =
|
||||
skew(t)
|
||||
split(t)
|
||||
|
||||
proc del(a: var MemRegion, t: var PAvlNode, x: int) {.benign.} =
|
||||
proc del(a: var MemRegion, t: var PAvlNode, x: int) {.gcsafe.} =
|
||||
if isBottom(t): return
|
||||
a.last = t
|
||||
if x <% t.key:
|
||||
|
||||
@@ -49,20 +49,6 @@ when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
|
||||
when not declaredInScope(PageShift):
|
||||
include bitmasks
|
||||
|
||||
else:
|
||||
type
|
||||
RefCount = int
|
||||
|
||||
Cell {.pure.} = object
|
||||
refcount: RefCount # the refcount and some flags
|
||||
typ: PNimType
|
||||
when trackAllocationSource:
|
||||
filename: cstring
|
||||
line: int
|
||||
when useCellIds:
|
||||
id: int
|
||||
|
||||
PCell = ptr Cell
|
||||
|
||||
type
|
||||
PPageDesc = ptr PageDesc
|
||||
|
||||
@@ -138,11 +138,11 @@
|
||||
## localChannelExample() # "Hello from the main thread!"
|
||||
## ```
|
||||
|
||||
{.push raises: [], gcsafe.}
|
||||
|
||||
when not declared(ThisIsSystem):
|
||||
{.error: "You must not import this module explicitly".}
|
||||
|
||||
import std/private/syslocks
|
||||
|
||||
type
|
||||
pbytes = ptr UncheckedArray[byte]
|
||||
RawChannel {.pure, final.} = object ## msg queue for a thread
|
||||
@@ -181,10 +181,10 @@ proc deinitRawChannel(p: pointer) =
|
||||
|
||||
when not usesDestructors:
|
||||
proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
|
||||
mode: LoadStoreMode) {.benign.}
|
||||
mode: LoadStoreMode) {.gcsafe.}
|
||||
|
||||
proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
|
||||
mode: LoadStoreMode) {.benign.} =
|
||||
mode: LoadStoreMode) {.gcsafe.} =
|
||||
var
|
||||
d = cast[int](dest)
|
||||
s = cast[int](src)
|
||||
@@ -365,23 +365,35 @@ proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool
|
||||
releaseSys(q.lock)
|
||||
result = true
|
||||
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: sink TMsg) {.inline.} =
|
||||
## Sends a message to a thread. `msg` is deeply copied.
|
||||
discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
|
||||
when defined(gcDestructors):
|
||||
when defined(gcDestructors):
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: sink TMsg) {.inline.} =
|
||||
## Sends a message to a thread.
|
||||
discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
|
||||
wasMoved(msg)
|
||||
|
||||
proc trySend*[TMsg](c: var Channel[TMsg], msg: sink TMsg): bool {.inline.} =
|
||||
## Tries to send a message to a thread.
|
||||
##
|
||||
## `msg` is deeply copied. Doesn't block.
|
||||
##
|
||||
## Returns `false` if the message was not sent because number of pending items
|
||||
## in the channel exceeded `maxItems`.
|
||||
result = sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
|
||||
when defined(gcDestructors):
|
||||
proc trySend*[TMsg](c: var Channel[TMsg], msg: sink TMsg): bool {.inline.} =
|
||||
## Tries to send a message to a thread.
|
||||
##
|
||||
## Doesn't block.
|
||||
##
|
||||
## Returns `false` if the message was not sent because number of pending items
|
||||
## in the channel exceeded `maxItems`.
|
||||
result = sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
|
||||
if result:
|
||||
wasMoved(msg)
|
||||
else:
|
||||
proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} =
|
||||
## Sends a message to a thread. `msg` is deeply copied.
|
||||
discard sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), false)
|
||||
|
||||
proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} =
|
||||
## Tries to send a message to a thread.
|
||||
##
|
||||
## `msg` is deeply copied. Doesn't block.
|
||||
##
|
||||
## Returns `false` if the message was not sent because number of pending items
|
||||
## in the channel exceeded `maxItems`.
|
||||
result = sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true)
|
||||
|
||||
proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
|
||||
q.ready = true
|
||||
@@ -390,7 +402,7 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
|
||||
q.ready = false
|
||||
if typ != q.elemType:
|
||||
releaseSys(q.lock)
|
||||
raise newException(ValueError, "cannot receive message of wrong type")
|
||||
raiseAssert "cannot receive message of wrong type"
|
||||
rawRecv(q, res, typ)
|
||||
if q.maxItems > 0 and q.count == q.maxItems - 1:
|
||||
# Parent thread is awaiting in send. Wake it up.
|
||||
@@ -455,3 +467,5 @@ proc ready*[TMsg](c: var Channel[TMsg]): bool =
|
||||
## new messages.
|
||||
var q = cast[PRawChannel](addr(c))
|
||||
result = q.ready
|
||||
|
||||
{.pop.}
|
||||
@@ -38,6 +38,21 @@ proc `==`*[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.}
|
||||
proc `==`*[T: proc | iterator](x, y: T): bool {.magic: "EqProc", noSideEffect.}
|
||||
## Checks that two `proc` variables refer to the same procedure.
|
||||
|
||||
when true:
|
||||
# guard against string converted to cstring implicitly; see also #bug #25488
|
||||
proc isNil*(x: string): bool {.noSideEffect, error: "'isNil' is invalid for 'string'".}
|
||||
|
||||
|
||||
# bug #9149; ensure that 'typeof(nil)' does not match *too* well by using 'typeof(nil) | typeof(nil)',
|
||||
# especially for converters, see tests/overload/tconverter_to_string.nim
|
||||
# Eventually we will be able to remove this hack completely.
|
||||
|
||||
proc `==`*(x: string; y: typeof(nil) | typeof(nil)): bool {.error: "'nil' is invalid for 'string'".} =
|
||||
discard
|
||||
|
||||
proc `==`*(x: typeof(nil) | typeof(nil); y: string): bool {.error: "'nil' is invalid for 'string'".} =
|
||||
discard
|
||||
|
||||
proc `<=`*[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.}
|
||||
proc `<=`*(x, y: string): bool {.magic: "LeStr", noSideEffect.} =
|
||||
## Compares two strings and returns true if `x` is lexicographically
|
||||
|
||||
@@ -10,7 +10,7 @@ const
|
||||
## is the minor number of Nim's version.
|
||||
## Odd for devel, even for releases.
|
||||
|
||||
NimPatch* {.intdefine.}: int = 6
|
||||
NimPatch* {.intdefine.}: int = 8
|
||||
## is the patch number of Nim's version.
|
||||
## Odd for devel, even for releases.
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ const
|
||||
colorMask = 0b011
|
||||
|
||||
type
|
||||
TraceProc = proc (p, env: pointer) {.nimcall, benign, raises: [].}
|
||||
DisposeProc = proc (p: pointer) {.nimcall, benign, raises: [].}
|
||||
TraceProc = proc (p, env: pointer) {.nimcall, gcsafe, raises: [].}
|
||||
DisposeProc = proc (p: pointer) {.nimcall, gcsafe, raises: [].}
|
||||
|
||||
template color(c): untyped = c.rc and colorMask
|
||||
template setColor(c, col) =
|
||||
|
||||
@@ -58,9 +58,9 @@ proc put(t: var PtrTable; key, val: pointer) =
|
||||
inc t.counter
|
||||
|
||||
proc genericDeepCopyAux(dest, src: pointer, mt: PNimType;
|
||||
tab: var PtrTable) {.benign.}
|
||||
tab: var PtrTable) {.gcsafe.}
|
||||
proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
|
||||
tab: var PtrTable) {.benign.} =
|
||||
tab: var PtrTable) {.gcsafe.} =
|
||||
var
|
||||
d = cast[int](dest)
|
||||
s = cast[int](src)
|
||||
|
||||
@@ -16,7 +16,7 @@ import stacktraces
|
||||
const noStacktraceAvailable = "No stack traceback available\n"
|
||||
|
||||
var
|
||||
errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign,
|
||||
errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], gcsafe,
|
||||
nimcall, raises: [].})
|
||||
## Function that will be called
|
||||
## instead of `stdmsg.write` when printing stacktrace.
|
||||
@@ -42,18 +42,22 @@ proc writeToStdErr(msg: string) {.inline.} =
|
||||
# fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring
|
||||
writeToStdErr(msg.cstring, msg.len)
|
||||
|
||||
proc cstrToStrBuiltin(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
|
||||
when defined(genode):
|
||||
template `$`(s: string): string = s
|
||||
|
||||
proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
|
||||
var toWrite = true
|
||||
if errorMessageWriter != nil:
|
||||
try:
|
||||
errorMessageWriter($data)
|
||||
errorMessageWriter(cstrToStrBuiltin data)
|
||||
toWrite = false
|
||||
except:
|
||||
discard
|
||||
if toWrite:
|
||||
when defined(genode):
|
||||
# stderr not available by default, use the LOG session
|
||||
echo data
|
||||
echo cstrToStrBuiltin(data)
|
||||
else:
|
||||
writeToStdErr(data, length)
|
||||
|
||||
@@ -61,10 +65,10 @@ proc showErrorMessage2(data: string) {.inline.} =
|
||||
# TODO showErrorMessage will turn it back to a string when a hook is set (!)
|
||||
showErrorMessage(data.cstring, data.len)
|
||||
|
||||
proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
|
||||
proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
|
||||
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
|
||||
proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
|
||||
proc chckIndx(i, a, b: int): int {.inline, compilerproc, gcsafe.}
|
||||
proc chckRange(i, a, b: int): int {.inline, compilerproc, gcsafe.}
|
||||
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, gcsafe.}
|
||||
proc chckNil(p: pointer) {.noinline, compilerproc, gcsafe.}
|
||||
|
||||
type
|
||||
GcFrame = ptr GcFrameHeader
|
||||
@@ -261,7 +265,10 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
|
||||
var oldLen = s.len
|
||||
s.toLocation(f.filename, f.line, 0)
|
||||
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
|
||||
add(s, f.procname)
|
||||
var i = 0
|
||||
while f.procname[i] != '\0':
|
||||
add(s, f.procname[i])
|
||||
inc i
|
||||
when NimStackTraceMsgs:
|
||||
when typeof(f) is StackTraceEntry:
|
||||
add(s, f.frameMsg)
|
||||
@@ -282,9 +289,35 @@ proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
|
||||
elif s[i].line == reraisedFromEnd: result.add "]]\n"
|
||||
else: addFrameEntry(result, s[i])
|
||||
|
||||
when hasSomeStackTrace:
|
||||
const
|
||||
Ten = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
|
||||
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||
proc i2s(x: int64): string =
|
||||
# quick reimplementation; optimized for code size, no dependencies
|
||||
if x < 0:
|
||||
if x == -9223372036854775808:
|
||||
result = "-9223372036854775808"
|
||||
else:
|
||||
result = "-" & i2s(0-x)
|
||||
elif x < 10:
|
||||
result = Ten[int x] # saves allocations
|
||||
else:
|
||||
var y = x
|
||||
while true:
|
||||
result.add char((y mod 10) + int('0'))
|
||||
y = y div 10
|
||||
if y == 0: break
|
||||
let last = result.len-1
|
||||
var i = 0
|
||||
let b = result.len div 2
|
||||
while i < b:
|
||||
let ch = result[i]
|
||||
result[i] = result[last-i]
|
||||
result[last-i] = ch
|
||||
inc i
|
||||
|
||||
when hasSomeStackTrace:
|
||||
proc auxWriteStackTrace(f: PFrame, s: var string) {.raises: [].} =
|
||||
when hasThreadSupport:
|
||||
var
|
||||
tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
|
||||
@@ -322,14 +355,14 @@ when hasSomeStackTrace:
|
||||
for j in countdown(i-1, 0):
|
||||
if tempFrames[j] == nil:
|
||||
add(s, "(")
|
||||
add(s, $skipped)
|
||||
s.add(i2s(skipped))
|
||||
add(s, " calls omitted) ...\n")
|
||||
else:
|
||||
addFrameEntry(s, tempFrames[j])
|
||||
|
||||
proc stackTraceAvailable*(): bool
|
||||
|
||||
proc rawWriteStackTrace(s: var string) =
|
||||
proc rawWriteStackTrace(s: var string) {.raises: [].} =
|
||||
when defined(nimStackTraceOverride):
|
||||
add(s, "Traceback (most recent call last, using override)\n")
|
||||
auxWriteStackTraceWithOverride(s)
|
||||
@@ -388,7 +421,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
|
||||
add(buf, "Error: unhandled exception: ")
|
||||
add(buf, e.msg)
|
||||
add(buf, " [")
|
||||
add(buf, $e.name)
|
||||
add(buf, cstrToStrBuiltin(e.name))
|
||||
add(buf, "]\n")
|
||||
|
||||
if onUnhandledException != nil:
|
||||
@@ -418,7 +451,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
|
||||
xadd(buf, e.name, e.name.len)
|
||||
add(buf, "]\n")
|
||||
if onUnhandledException != nil:
|
||||
onUnhandledException($cast[cstring](buf.addr))
|
||||
onUnhandledException(cstrToStrBuiltin(cast[cstring](buf.addr)))
|
||||
else:
|
||||
showErrorMessage(cast[cstring](buf.addr), L)
|
||||
|
||||
@@ -515,8 +548,7 @@ proc reraiseException() {.compilerRtl.} =
|
||||
else:
|
||||
raiseExceptionAux(currException)
|
||||
|
||||
proc threadTrouble() =
|
||||
# also forward declared, it is 'raises: []' hence the try-except.
|
||||
proc threadTrouble() {.raises: [], gcsafe.} =
|
||||
try:
|
||||
if currException != nil: reportUnhandledError(currException)
|
||||
except:
|
||||
@@ -559,13 +591,16 @@ const nimCallDepthLimit {.intdefine.} = 2000
|
||||
|
||||
proc callDepthLimitReached() {.noinline.} =
|
||||
writeStackTrace()
|
||||
let msg = "Error: call depth limit reached in a debug build (" &
|
||||
$nimCallDepthLimit & " function calls). You can change it with " &
|
||||
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
|
||||
"recursions instead.\n"
|
||||
var msg = "Error: call depth limit reached in a debug build ("
|
||||
msg.add(i2s(nimCallDepthLimit))
|
||||
msg.add(" function calls). You can change it with " &
|
||||
"-d:nimCallDepthLimit=<int> but really try to avoid deep " &
|
||||
"recursions instead.\n")
|
||||
showErrorMessage2(msg)
|
||||
rawQuit(1)
|
||||
|
||||
{.push overflowChecks: off.}
|
||||
|
||||
proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
|
||||
if framePtr == nil:
|
||||
s.calldepth = 0
|
||||
@@ -577,6 +612,8 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
|
||||
framePtr = s
|
||||
if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
|
||||
|
||||
{.pop.}
|
||||
|
||||
when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
|
||||
not defined(js) and not defined(nimscript) and
|
||||
hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and
|
||||
@@ -601,9 +638,9 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
|
||||
{.emit: "#endif".}
|
||||
except Exception:
|
||||
msg = currException.getStackTrace() & "Error: unhandled exception: " &
|
||||
currException.msg & " [" & $currException.name & "]"
|
||||
currException.msg & " [" & cstrToStrBuiltin(currException.name) & "]"
|
||||
except StdException as e:
|
||||
msg = "Error: unhandled cpp exception: " & $e.what()
|
||||
msg = "Error: unhandled cpp exception: " & cstrToStrBuiltin(e.what())
|
||||
except:
|
||||
msg = "Error: unhandled unknown cpp exception"
|
||||
|
||||
@@ -620,7 +657,7 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
|
||||
rawQuit 1
|
||||
|
||||
when not defined(noSignalHandler) and not defined(useNimRtl):
|
||||
type Sighandler = proc (a: cint) {.noconv, benign.}
|
||||
type Sighandler = proc (a: cint) {.noconv, gcsafe.}
|
||||
# xxx factor with ansi_c.CSighandlerT, posix.Sighandler
|
||||
|
||||
proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv, raises: [].} =
|
||||
|
||||
@@ -14,14 +14,23 @@ const
|
||||
quirkyExceptions = compileOption("exceptions", "quirky")
|
||||
|
||||
when hostOS == "standalone":
|
||||
include "$projectpath/panicoverride"
|
||||
# These procs are defined in panicoverride.nim, which gets included at end
|
||||
# of system.nim with exportc.
|
||||
proc nimPanic(msg: string) {.importc: "nimPanic", noreturn.}
|
||||
proc nimRawoutput(msg: string) {.importc: "nimRawoutput".}
|
||||
|
||||
func sysFatal(exceptn: typedesc[Defect], message: string) {.inline.} =
|
||||
panic(message)
|
||||
proc sysFatal(exceptn: typedesc[Defect], message: string) {.inline, noreturn, raises: [], tags: [].} =
|
||||
{.cast(noSideEffect).}:
|
||||
{.cast(raises: []).}:
|
||||
{.cast(tags: []).}:
|
||||
nimPanic(message)
|
||||
|
||||
func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline.} =
|
||||
rawoutput(message)
|
||||
panic(arg)
|
||||
proc sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline, noreturn, raises: [], tags: [].} =
|
||||
{.cast(noSideEffect).}:
|
||||
{.cast(raises: []).}:
|
||||
{.cast(tags: []).}:
|
||||
nimRawoutput(message)
|
||||
nimPanic(arg)
|
||||
|
||||
elif quirkyExceptions and not defined(nimscript):
|
||||
import ansi_c
|
||||
|
||||
@@ -76,7 +76,7 @@ const
|
||||
when withRealTime and not declared(getTicks):
|
||||
include "system/timers"
|
||||
when defined(memProfiler):
|
||||
proc nimProfile(requestedSize: int) {.benign.}
|
||||
proc nimProfile(requestedSize: int) {.gcsafe.}
|
||||
|
||||
when hasThreadSupport:
|
||||
import std/sharedlist
|
||||
@@ -97,7 +97,7 @@ type
|
||||
waZctDecRef, waPush
|
||||
#, waDebug
|
||||
|
||||
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].}
|
||||
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe, raises: [].}
|
||||
# A ref type can have a finalizer that is called before the object's
|
||||
# storage is freed.
|
||||
|
||||
@@ -222,11 +222,11 @@ template gcTrace(cell, state: untyped) =
|
||||
when traceGC: traceCell(cell, state)
|
||||
|
||||
# forward declarations:
|
||||
proc collectCT(gch: var GcHeap) {.benign, raises: [].}
|
||||
proc isOnStack(p: pointer): bool {.noinline, benign, raises: [].}
|
||||
proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].}
|
||||
proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].}
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].}
|
||||
proc collectCT(gch: var GcHeap) {.gcsafe, raises: [].}
|
||||
proc isOnStack(p: pointer): bool {.noinline, gcsafe, raises: [].}
|
||||
proc forAllChildren(cell: PCell, op: WalkOp) {.gcsafe, raises: [].}
|
||||
proc doOperation(p: pointer, op: WalkOp) {.gcsafe, raises: [].}
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.gcsafe, raises: [].}
|
||||
# we need the prototype here for debugging purposes
|
||||
|
||||
proc incRef(c: PCell) {.inline.} =
|
||||
@@ -338,7 +338,7 @@ proc cellsetReset(s: var CellSet) =
|
||||
|
||||
{.push stacktrace:off.}
|
||||
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.gcsafe.} =
|
||||
var d = cast[int](dest)
|
||||
case n.kind
|
||||
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
|
||||
@@ -458,9 +458,16 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||
collectCT(gch)
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
# Use alignment from typ.base if available, otherwise use MemAlign
|
||||
let alignment = if typ.kind == tyRef and typ.base != nil and
|
||||
typ.base.align >= MemAlign: typ.base.align else: 0
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell), alignment))
|
||||
#gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
|
||||
gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
# Check that the user data (after the Cell header) is properly aligned
|
||||
if alignment == 0:
|
||||
gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2.1")
|
||||
else:
|
||||
gcAssert((cast[int](cellToUsr(res)) and (alignment-1)) == 0, "newObj: 2.2")
|
||||
# now it is buffered in the ZCT
|
||||
res.typ = typ
|
||||
setFrameInfo(res)
|
||||
@@ -481,17 +488,17 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
|
||||
{.pop.} # .stackTrace off
|
||||
{.pop.} # .profiler off
|
||||
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
result = rawNewObj(typ, size, gch)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline, raises: [].} =
|
||||
result = rawNewObj(typ, size, gch)
|
||||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
|
||||
# `newObj` already uses locks, so no need for them here.
|
||||
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
|
||||
result = newObj(typ, size)
|
||||
@@ -500,7 +507,7 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
{.pop.}
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline, raises: [].} =
|
||||
# generates a new object and sets its reference counter to 1
|
||||
incTypeSize typ, size
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 begin")
|
||||
@@ -508,9 +515,16 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
|
||||
collectCT(gch)
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after collectCT")
|
||||
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
|
||||
# Use alignment from typ.base if available, otherwise use MemAlign
|
||||
let alignment = if typ.kind == tyRef and typ.base != nil and
|
||||
typ.base.align >= MemAlign: typ.base.align else: 0
|
||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell), alignment))
|
||||
sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
|
||||
sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||
# Check that the user data (after the Cell header) is properly aligned
|
||||
if alignment == 0:
|
||||
sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2.1")
|
||||
else:
|
||||
sysAssert((cast[int](cellToUsr(res)) and (alignment-1)) == 0, "newObj: 2.2")
|
||||
# now it is buffered in the ZCT
|
||||
res.typ = typ
|
||||
setFrameInfo(res)
|
||||
@@ -528,7 +542,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
|
||||
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
|
||||
result = newObjRC1(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
@@ -670,10 +684,10 @@ proc doOperation(p: pointer, op: WalkOp) =
|
||||
add(gch.tempStack, c)
|
||||
#of waDebug: debugGraph(c)
|
||||
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl, raises: [].} =
|
||||
doOperation(d, WalkOp(op))
|
||||
|
||||
proc collectZCT(gch: var GcHeap): bool {.benign, raises: [].}
|
||||
proc collectZCT(gch: var GcHeap): bool {.gcsafe, raises: [].}
|
||||
|
||||
proc collectCycles(gch: var GcHeap) {.raises: [].} =
|
||||
when hasThreadSupport:
|
||||
@@ -916,4 +930,4 @@ when not defined(useNimRtl):
|
||||
result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
|
||||
|
||||
{.pop.} # raises: []
|
||||
{.pop.} # profiler: off, stackTrace: off
|
||||
{.pop.} # profiler: off, stackTrace: off
|
||||
@@ -457,7 +457,7 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) =
|
||||
initGC()
|
||||
|
||||
type
|
||||
GlobalMarkerProc = proc () {.nimcall, benign, raises: [].}
|
||||
GlobalMarkerProc = proc () {.nimcall, gcsafe, raises: [].}
|
||||
var
|
||||
globalMarkersLen {.exportc.}: int
|
||||
globalMarkers {.exportc.}: array[0..3499, GlobalMarkerProc]
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## collectors etc.
|
||||
|
||||
type
|
||||
GlobalMarkerProc = proc () {.nimcall, benign, raises: [], tags: [].}
|
||||
GlobalMarkerProc = proc () {.nimcall, gcsafe, raises: [], tags: [].}
|
||||
var
|
||||
globalMarkersLen: int
|
||||
globalMarkers: array[0..3499, GlobalMarkerProc]
|
||||
@@ -46,8 +46,8 @@ var
|
||||
newObjHook*: proc (typ: PNimType, size: int): pointer {.nimcall, tags: [], raises: [], gcsafe.}
|
||||
traverseObjHook*: proc (p: pointer, op: int) {.nimcall, tags: [], raises: [], gcsafe.}
|
||||
|
||||
proc nimGCvisit(p: pointer, op: int) {.inl, compilerRtl.} =
|
||||
proc nimGCvisit(p: pointer, op: int) {.inl, compilerRtl, raises: [].} =
|
||||
traverseObjHook(p, op)
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.inl, compilerRtl.} =
|
||||
proc newObj(typ: PNimType, size: int): pointer {.inl, compilerRtl, raises: [].} =
|
||||
result = newObjHook(typ, size)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# ----------------- GC interface ---------------------------------------------
|
||||
const
|
||||
usesDestructors = defined(gcDestructors) or defined(gcHooks)
|
||||
|
||||
when not usesDestructors:
|
||||
{.pragma: nodestroy.}
|
||||
@@ -14,7 +12,7 @@ when hasAlloc:
|
||||
gcOptimizeSpace ## optimize for memory footprint
|
||||
|
||||
when hasAlloc and not defined(js) and not usesDestructors:
|
||||
proc GC_disable*() {.rtl, inl, benign, raises: [].}
|
||||
proc GC_disable*() {.rtl, inl, gcsafe, raises: [].}
|
||||
## Disables the GC. If called `n` times, `n` calls to `GC_enable`
|
||||
## are needed to reactivate the GC.
|
||||
##
|
||||
@@ -22,39 +20,39 @@ when hasAlloc and not defined(js) and not usesDestructors:
|
||||
## the mark and sweep phase with
|
||||
## `GC_disableMarkAndSweep <#GC_disableMarkAndSweep>`_.
|
||||
|
||||
proc GC_enable*() {.rtl, inl, benign, raises: [].}
|
||||
proc GC_enable*() {.rtl, inl, gcsafe, raises: [].}
|
||||
## Enables the GC again.
|
||||
|
||||
proc GC_fullCollect*() {.rtl, benign, raises: [].}
|
||||
proc GC_fullCollect*() {.rtl, gcsafe, raises: [].}
|
||||
## Forces a full garbage collection pass.
|
||||
## Ordinary code does not need to call this (and should not).
|
||||
|
||||
proc GC_enableMarkAndSweep*() {.rtl, benign, raises: [].}
|
||||
proc GC_disableMarkAndSweep*() {.rtl, benign, raises: [].}
|
||||
proc GC_enableMarkAndSweep*() {.rtl, gcsafe, raises: [].}
|
||||
proc GC_disableMarkAndSweep*() {.rtl, gcsafe, raises: [].}
|
||||
## The current implementation uses a reference counting garbage collector
|
||||
## with a seldomly run mark and sweep phase to free cycles. The mark and
|
||||
## sweep phase may take a long time and is not needed if the application
|
||||
## does not create cycles. Thus the mark and sweep phase can be deactivated
|
||||
## and activated separately from the rest of the GC.
|
||||
|
||||
proc GC_getStatistics*(): string {.rtl, benign, raises: [].}
|
||||
proc GC_getStatistics*(): string {.rtl, gcsafe, raises: [].}
|
||||
## Returns an informative string about the GC's activity. This may be useful
|
||||
## for tweaking.
|
||||
|
||||
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign, raises: [].}
|
||||
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign, raises: [].}
|
||||
proc GC_ref*(x: string) {.magic: "GCref", benign, raises: [].}
|
||||
proc GC_ref*[T](x: ref T) {.magic: "GCref", gcsafe, raises: [].}
|
||||
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", gcsafe, raises: [].}
|
||||
proc GC_ref*(x: string) {.magic: "GCref", gcsafe, raises: [].}
|
||||
## Marks the object `x` as referenced, so that it will not be freed until
|
||||
## it is unmarked via `GC_unref`.
|
||||
## If called n-times for the same object `x`,
|
||||
## n calls to `GC_unref` are needed to unmark `x`.
|
||||
|
||||
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign, raises: [].}
|
||||
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign, raises: [].}
|
||||
proc GC_unref*(x: string) {.magic: "GCunref", benign, raises: [].}
|
||||
proc GC_unref*[T](x: ref T) {.magic: "GCunref", gcsafe, raises: [].}
|
||||
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", gcsafe, raises: [].}
|
||||
proc GC_unref*(x: string) {.magic: "GCunref", gcsafe, raises: [].}
|
||||
## See the documentation of `GC_ref <#GC_ref,string>`_.
|
||||
|
||||
proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, benign, raises: [].}
|
||||
proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, gcsafe, raises: [].}
|
||||
## Expands operating GC stack range to `theStackBottom`. Does nothing
|
||||
## if current stack bottom is already lower than `theStackBottom`.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ type
|
||||
# local
|
||||
waMarkPrecise # fast precise marking
|
||||
|
||||
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].}
|
||||
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe, raises: [].}
|
||||
# A ref type can have a finalizer that is called before the object's
|
||||
# storage is freed.
|
||||
|
||||
@@ -115,10 +115,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||
|
||||
# forward declarations:
|
||||
proc collectCT(gch: var GcHeap; size: int) {.benign, raises: [].}
|
||||
proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].}
|
||||
proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].}
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].}
|
||||
proc collectCT(gch: var GcHeap; size: int) {.gcsafe, raises: [].}
|
||||
proc forAllChildren(cell: PCell, op: WalkOp) {.gcsafe, raises: [].}
|
||||
proc doOperation(p: pointer, op: WalkOp) {.gcsafe, raises: [].}
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.gcsafe, raises: [].}
|
||||
# we need the prototype here for debugging purposes
|
||||
|
||||
when defined(nimGcRefLeak):
|
||||
@@ -216,7 +216,7 @@ proc initGC() =
|
||||
gch.gcThreadId = atomicInc(gHeapidGenerator) - 1
|
||||
gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID")
|
||||
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.gcsafe.} =
|
||||
var d = cast[int](dest)
|
||||
case n.kind
|
||||
of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
|
||||
@@ -289,23 +289,23 @@ when useCellIds:
|
||||
|
||||
{.pop.}
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
result = rawNewObj(typ, size, gch)
|
||||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
result = rawNewObj(typ, size, gch)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
result = rawNewObj(typ, size, gch)
|
||||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
when not defined(nimSeqsV2):
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
|
||||
# `newObj` already uses locks, so no need for them here.
|
||||
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
|
||||
result = newObj(typ, size)
|
||||
@@ -313,7 +313,7 @@ when not defined(nimSeqsV2):
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
|
||||
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
|
||||
result = newObj(typ, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
@@ -346,7 +346,7 @@ when not defined(nimSeqsV2):
|
||||
result = cellToUsr(res)
|
||||
when defined(memProfiler): nimProfile(newsize-oldsize)
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl, raises: [].} =
|
||||
result = growObj(old, newsize, gch)
|
||||
|
||||
{.push profiler:off.}
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
{.push raises: [], gcsafe.}
|
||||
|
||||
# "Stack GC" for embedded devices or ultra performance requirements.
|
||||
import std/private/syslocks
|
||||
|
||||
when defined(memProfiler):
|
||||
proc nimProfile(requestedSize: int) {.benign.}
|
||||
proc nimProfile(requestedSize: int) {.gcsafe.}
|
||||
|
||||
when defined(useMalloc):
|
||||
proc roundup(x, v: int): int {.inline.} =
|
||||
@@ -39,7 +41,7 @@ else:
|
||||
# We also support 'finalizers'.
|
||||
|
||||
type
|
||||
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
|
||||
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, gcsafe, raises: [].}
|
||||
# A ref type can have a finalizer that is called before the object's
|
||||
# storage is freed.
|
||||
|
||||
@@ -305,26 +307,26 @@ proc rawNewSeq(r: var MemRegion, typ: PNimType, size: int): pointer =
|
||||
res.region = addr(r)
|
||||
result = res +! sizeof(SeqHeader)
|
||||
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
sysAssert typ.kind notin {tySequence, tyString}, "newObj cannot be used to construct seqs"
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
sysAssert typ.kind notin {tySequence, tyString}, "newObj cannot be used to construct seqs"
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
when defined(memProfiler): nimProfile(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
|
||||
let size = roundup(align(GenericSeqSize, typ.base.align) + len * typ.base.size, MemAlign)
|
||||
result = rawNewSeq(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
|
||||
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
|
||||
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl, raises: [].} =
|
||||
let size = roundup(len + GenericSeqSize, MemAlign)
|
||||
result = rawNewSeq(tlRegion, typ, size)
|
||||
if init: zeroMem(result, size)
|
||||
@@ -332,14 +334,14 @@ proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
{.pop.}
|
||||
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
|
||||
result = rawNewObj(tlRegion, typ, size)
|
||||
zeroMem(result, size)
|
||||
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
|
||||
result = newSeq(typ, len)
|
||||
|
||||
proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
|
||||
proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer {.raises: [].} =
|
||||
let sh = cast[ptr SeqHeader](old -! sizeof(SeqHeader))
|
||||
let typ = sh.typ
|
||||
result = rawNewSeq(sh.region[], typ,
|
||||
@@ -351,7 +353,7 @@ proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
|
||||
copyMem(result, old, oldsize)
|
||||
dealloc(sh.region[], old, roundup(oldsize, MemAlign))
|
||||
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||
proc growObj(old: pointer, newsize: int): pointer {.rtl, raises: [].} =
|
||||
result = growObj(tlRegion, old, newsize)
|
||||
|
||||
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
|
||||
@@ -434,3 +436,5 @@ proc nimGC_setStackBottom(theStackBottom: pointer) = discard
|
||||
|
||||
proc nimGCref(x: pointer) {.compilerproc.} = discard
|
||||
proc nimGCunref(x: pointer) {.compilerproc.} = discard
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -96,8 +96,8 @@ type
|
||||
base*: ptr TNimType
|
||||
node: ptr TNimNode # valid for tyRecord, tyObject, tyTuple, tyEnum
|
||||
finalizer*: pointer # the finalizer for the type
|
||||
marker*: proc (p: pointer, op: int) {.nimcall, benign, tags: [], raises: [].} # marker proc for GC
|
||||
deepcopy: proc (p: pointer): pointer {.nimcall, benign, tags: [], raises: [].}
|
||||
marker*: proc (p: pointer, op: int) {.nimcall, gcsafe, tags: [], raises: [].} # marker proc for GC
|
||||
deepcopy: proc (p: pointer): pointer {.nimcall, gcsafe, tags: [], raises: [].}
|
||||
when defined(nimSeqsV2):
|
||||
typeInfoV2*: pointer
|
||||
when defined(nimTypeNames):
|
||||
|
||||
@@ -51,7 +51,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
|
||||
proc isNimException(): bool {.asmNoStackFrame.} =
|
||||
{.emit: "return `lastJSError` && `lastJSError`.m_type;".}
|
||||
|
||||
proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
|
||||
proc getCurrentException*(): ref Exception {.compilerRtl, gcsafe.} =
|
||||
if isNimException(): result = cast[ref Exception](lastJSError)
|
||||
|
||||
proc getCurrentExceptionMsg*(): string =
|
||||
@@ -72,7 +72,7 @@ proc getCurrentExceptionMsg*(): string =
|
||||
proc setCurrentException*(exc: ref Exception) =
|
||||
lastJSError = cast[PJSError](exc)
|
||||
|
||||
proc closureIterSetExc(e: ref Exception) {.compilerRtl, benign.} =
|
||||
proc closureIterSetExc(e: ref Exception) {.compilerRtl, gcsafe.} =
|
||||
setCurrentException(e)
|
||||
|
||||
proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inline.} =
|
||||
@@ -687,6 +687,14 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
|
||||
proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
|
||||
{.emit: "`x`.push(`c`);".}
|
||||
|
||||
proc nimAddStrStr(x, y: string) {.compilerproc, asmNoStackFrame.} =
|
||||
{.emit: """
|
||||
var L = `y`.length;
|
||||
for (var i = 0; i < L; ++i) {
|
||||
`x`.push(`y`[i]);
|
||||
}
|
||||
""".}
|
||||
|
||||
{.pop.}
|
||||
|
||||
proc tenToThePowerOf(b: int): BiggestFloat =
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
when notJSnotNims:
|
||||
proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Overwrites the contents of the memory at `p` with the value 0.
|
||||
##
|
||||
## Exactly `size` bytes will be overwritten. Like any procedure
|
||||
## dealing with raw memory this is **unsafe**.
|
||||
|
||||
proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||
tags: [], raises: [].}
|
||||
proc copyMem*(dest, source: pointer, size: Natural) {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Copies the contents from the memory at `source` to the memory
|
||||
## at `dest`.
|
||||
## Exactly `size` bytes will be copied. The memory
|
||||
## regions may not overlap. Like any procedure dealing with raw
|
||||
## memory this is **unsafe**.
|
||||
|
||||
proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
|
||||
tags: [], raises: [].}
|
||||
proc moveMem*(dest, source: pointer, size: Natural) {.inline, gcsafe,
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Copies the contents from the memory at `source` to the memory
|
||||
## at `dest`.
|
||||
##
|
||||
@@ -25,7 +25,7 @@ when notJSnotNims:
|
||||
## dealing with raw memory this is still **unsafe**, though.
|
||||
|
||||
proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Compares the memory blocks `a` and `b`. `size` bytes will
|
||||
## be compared.
|
||||
##
|
||||
@@ -34,7 +34,7 @@ when notJSnotNims:
|
||||
## **unsafe**.
|
||||
|
||||
proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
|
||||
tags: [], raises: [].}
|
||||
tags: [], raises: [], enforceNoRaises.}
|
||||
## Compares the memory blocks `a` and `b`. `size` bytes will
|
||||
## be compared.
|
||||
##
|
||||
@@ -48,17 +48,17 @@ when notJSnotNims:
|
||||
|
||||
when hasAlloc and not defined(js):
|
||||
|
||||
proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc alloc0Impl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc deallocImpl*(p: pointer) {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc reallocImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc realloc0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
proc alloc0Impl*(size: Natural): pointer {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
proc deallocImpl*(p: pointer) {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
proc reallocImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
proc realloc0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
|
||||
proc allocSharedImpl*(size: Natural): pointer {.noconv, compilerproc, rtl, benign, raises: [], tags: [].}
|
||||
proc allocShared0Impl*(size: Natural): pointer {.noconv, rtl, benign, raises: [], tags: [].}
|
||||
proc deallocSharedImpl*(p: pointer) {.noconv, rtl, benign, raises: [], tags: [].}
|
||||
proc reallocSharedImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc reallocShared0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].}
|
||||
proc allocSharedImpl*(size: Natural): pointer {.noconv, compilerproc, rtl, gcsafe, raises: [], tags: [].}
|
||||
proc allocShared0Impl*(size: Natural): pointer {.noconv, rtl, gcsafe, raises: [], tags: [].}
|
||||
proc deallocSharedImpl*(p: pointer) {.noconv, rtl, gcsafe, raises: [], tags: [].}
|
||||
proc reallocSharedImpl*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
proc reallocShared0Impl*(p: pointer, oldSize, newSize: Natural): pointer {.noconv, rtl, tags: [], gcsafe, raises: [].}
|
||||
|
||||
# Allocator statistics for memory leak tests
|
||||
|
||||
@@ -103,7 +103,7 @@ when hasAlloc and not defined(js):
|
||||
incStat(allocCount)
|
||||
allocImpl(size)
|
||||
|
||||
proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} =
|
||||
proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, gcsafe, raises: [].} =
|
||||
## Allocates a new memory block with at least `T.sizeof * size` bytes.
|
||||
##
|
||||
## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_
|
||||
@@ -131,7 +131,7 @@ when hasAlloc and not defined(js):
|
||||
incStat(allocCount)
|
||||
alloc0Impl(size)
|
||||
|
||||
proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} =
|
||||
proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, gcsafe, raises: [].} =
|
||||
## Allocates a new memory block with at least `T.sizeof * size` bytes.
|
||||
##
|
||||
## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_
|
||||
@@ -174,7 +174,7 @@ when hasAlloc and not defined(js):
|
||||
## from a shared heap.
|
||||
realloc0Impl(p, oldSize, newSize)
|
||||
|
||||
proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, benign, raises: [].} =
|
||||
proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline, gcsafe, raises: [].} =
|
||||
## Grows or shrinks a given memory block.
|
||||
##
|
||||
## If `p` is **nil** then a new memory block is returned.
|
||||
@@ -187,7 +187,7 @@ when hasAlloc and not defined(js):
|
||||
## from a shared heap.
|
||||
cast[ptr T](realloc(p, T.sizeof * newSize))
|
||||
|
||||
proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} =
|
||||
proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, gcsafe, raises: [], tags: [].} =
|
||||
## Frees the memory allocated with `alloc`, `alloc0`,
|
||||
## `realloc`, `create` or `createU`.
|
||||
##
|
||||
@@ -218,7 +218,7 @@ when hasAlloc and not defined(js):
|
||||
allocSharedImpl(size)
|
||||
|
||||
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, tags: [],
|
||||
benign, raises: [].} =
|
||||
gcsafe, raises: [].} =
|
||||
## Allocates a new memory block on the shared heap with at
|
||||
## least `T.sizeof * size` bytes.
|
||||
##
|
||||
@@ -296,7 +296,7 @@ when hasAlloc and not defined(js):
|
||||
## `freeShared <#freeShared,ptr.T>`_.
|
||||
cast[ptr T](reallocShared(p, T.sizeof * newSize))
|
||||
|
||||
proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} =
|
||||
proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, gcsafe, raises: [], tags: [].} =
|
||||
## Frees the memory allocated with `allocShared`, `allocShared0` or
|
||||
## `reallocShared`.
|
||||
##
|
||||
@@ -307,7 +307,7 @@ when hasAlloc and not defined(js):
|
||||
incStat(deallocCount)
|
||||
deallocSharedImpl(p)
|
||||
|
||||
proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} =
|
||||
proc freeShared*[T](p: ptr T) {.inline, gcsafe, raises: [].} =
|
||||
## Frees the memory allocated with `createShared`, `createSharedU` or
|
||||
## `resizeShared`.
|
||||
##
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
const useLibC = not defined(nimNoLibc)
|
||||
|
||||
when useLibC:
|
||||
import ansi_c
|
||||
import ansi_c
|
||||
|
||||
proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, compilerproc, inline.} =
|
||||
proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, inline, enforceNoRaises.} =
|
||||
when useLibC:
|
||||
c_memcpy(dest, source, cast[csize_t](size))
|
||||
else:
|
||||
@@ -16,7 +15,7 @@ proc nimCopyMem*(dest, source: pointer, size: Natural) {.nonReloadable, compiler
|
||||
d[i] = s[i]
|
||||
inc i
|
||||
|
||||
proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline.} =
|
||||
proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline, enforceNoRaises.} =
|
||||
when useLibC:
|
||||
c_memset(a, v, cast[csize_t](size))
|
||||
else:
|
||||
@@ -27,10 +26,10 @@ proc nimSetMem*(a: pointer, v: cint, size: Natural) {.nonReloadable, inline.} =
|
||||
a[i] = v
|
||||
inc i
|
||||
|
||||
proc nimZeroMem*(p: pointer, size: Natural) {.compilerproc, nonReloadable, inline.} =
|
||||
proc nimZeroMem*(p: pointer, size: Natural) {.nonReloadable, inline, enforceNoRaises.} =
|
||||
nimSetMem(p, 0, size)
|
||||
|
||||
proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadable, inline.} =
|
||||
proc nimCmpMem*(a, b: pointer, size: Natural): cint {.nonReloadable, inline, enforceNoRaises.} =
|
||||
when useLibC:
|
||||
c_memcmp(a, b, cast[csize_t](size))
|
||||
else:
|
||||
@@ -42,7 +41,7 @@ proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadabl
|
||||
if d != 0: return d
|
||||
inc i
|
||||
|
||||
proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} =
|
||||
proc nimCStrLen*(a: cstring): int {.nonReloadable, inline, enforceNoRaises.} =
|
||||
if a.isNil: return 0
|
||||
when useLibC:
|
||||
cast[int](c_strlen(a))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
{.push raises: [], gcsafe.}
|
||||
|
||||
|
||||
proc boehmGCinit {.importc: "GC_init", boehmGC.}
|
||||
@@ -95,7 +95,7 @@ proc initGC() =
|
||||
when hasThreadSupport:
|
||||
boehmGC_allow_register_threads()
|
||||
|
||||
proc boehmgc_finalizer(obj: pointer, typedFinalizer: (proc(x: pointer) {.cdecl.})) =
|
||||
proc boehmgc_finalizer(obj: pointer, typedFinalizer: (proc(x: pointer) {.cdecl, raises: [], gcsafe.})) =
|
||||
typedFinalizer(obj)
|
||||
|
||||
|
||||
@@ -138,3 +138,5 @@ proc deallocOsPages(r: var MemRegion) {.inline.} = discard
|
||||
proc deallocOsPages() {.inline.} = discard
|
||||
|
||||
include "system/cellsets"
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{.push raises: [], gcsafe.}
|
||||
|
||||
when defined(windows):
|
||||
const goLib = "libgo.dll"
|
||||
@@ -151,3 +152,5 @@ proc alloc0(r: var MemRegion, size: int): pointer =
|
||||
proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
|
||||
proc deallocOsPages(r: var MemRegion) {.inline.} = discard
|
||||
proc deallocOsPages() {.inline.} = discard
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -20,7 +20,7 @@ proc newObjNoInit(typ: PNimType, size: int): pointer =
|
||||
result = alloc(size)
|
||||
|
||||
{.push overflowChecks: on.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc, raises: [].} =
|
||||
result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
|
||||
@@ -38,6 +38,21 @@ type
|
||||
PByte = ptr ByteArray
|
||||
PString = ptr string
|
||||
|
||||
when not defined(nimV2):
|
||||
type
|
||||
RefCount = int
|
||||
|
||||
Cell {.pure.} = object
|
||||
refcount: RefCount # the refcount and some flags
|
||||
typ: PNimType
|
||||
when trackAllocationSource:
|
||||
filename: cstring
|
||||
line: int
|
||||
when useCellIds:
|
||||
id: int
|
||||
|
||||
PCell = ptr Cell
|
||||
|
||||
when declared(IntsPerTrunk):
|
||||
discard
|
||||
else:
|
||||
@@ -60,10 +75,10 @@ elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc):
|
||||
|
||||
when defined(nogc):
|
||||
proc GC_getStatistics(): string = ""
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
|
||||
proc newObj(typ: PNimType, size: int): pointer {.compilerproc, raises: [].} =
|
||||
result = alloc0(size)
|
||||
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc, raises: [].} =
|
||||
result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).reserved = len
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user