init checks and 'out' parameters (#14521)

* I don't care about observable stores
* enforce explicit initializations
* cleaner code for the stdlib
* stdlib: use explicit initializations
* make tests green
* algorithm.nim: set result explicitly
* remove out parameters and bring the PR into a mergable state
* updated the changelog
This commit is contained in:
Andreas Rumpf 2020-06-23 10:53:57 +02:00 • committed by GitHub
commit da29222f86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 355 additions and 257 deletions

View file

@ -60,7 +60,7 @@
accept an existing string to modify, which avoids memory accept an existing string to modify, which avoids memory
allocations, similar to `streams.readLine` (#13857). allocations, similar to `streams.readLine` (#13857).
- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom`. UDP functionality. - Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality.
- `dollars.$` now works for unsigned ints with `nim js` - `dollars.$` now works for unsigned ints with `nim js`
@ -116,7 +116,11 @@
- Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
## Language changes ## Language changes
- In the newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this: - In the newruntime it is now allowed to assign to the discriminator field
without restrictions as long as case object doesn't have custom destructor.
The discriminator value doesn't have to be a constant either. If you have a
custom destructor for a case object and you do want to freely assign discriminator
fields, it is recommended to refactor object into 2 objects like this:
```nim ```nim
type type
@ -175,6 +179,8 @@ proc mydiv(a, b): int {.raises: [].} =
The reason for this is that `DivByZeroDefect` inherits from `Defect` and The reason for this is that `DivByZeroDefect` inherits from `Defect` and
with `--panics:on` `Defects` become unrecoverable errors. with `--panics:on` `Defects` become unrecoverable errors.
- Added the `thiscall` calling convention as specified by Microsoft.
- Added `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Added `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose
- Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop.

View file

@ -435,7 +435,8 @@ type
# be any type. # be any type.
tyOptDeprecated tyOptDeprecated
# deadcode: was `tyOpt`, Builtin optional type # 'out' parameter. Comparable to a 'var' parameter but every
# path must assign a value to it before it can be read from.
tyVoid tyVoid
# now different from tyEmpty, hurray! # now different from tyEmpty, hurray!
@ -1802,12 +1803,12 @@ proc skipStmtList*(n: PNode): PNode =
else: else:
result = n result = n
proc toVar*(typ: PType): PType = proc toVar*(typ: PType; kind: TTypeKind): PType =
## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and ## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is. ## returned. Otherwise ``typ`` is simply returned as-is.
result = typ result = typ
if typ.kind != tyVar: if typ.kind != kind:
result = newType(tyVar, typ.owner) result = newType(kind, typ.owner)
rawAddSon(result, typ) rawAddSon(result, typ)
proc toRef*(typ: PType): PType = proc toRef*(typ: PType): PType =
@ -1934,3 +1935,6 @@ proc toHumanStr*(kind: TSymKind): string =
proc toHumanStr*(kind: TTypeKind): string = proc toHumanStr*(kind: TTypeKind): string =
## strips leading `tk` ## strips leading `tk`
result = toHumanStrImpl(kind, 2) result = toHumanStrImpl(kind, 2)
proc skipAddr*(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n[0] else: n)

View file

@ -178,10 +178,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
of tyString, tySequence: of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst) let atyp = skipTypes(a.t, abstractInst)
if formalType.skipTypes(abstractInst).kind == tyVar and atyp.kind == tyString and if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
optSeqDestructors in p.config.globalOptions: optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if atyp.kind == tyVar and not compileToCpp(p.module): if atyp.kind in {tyVar} and not compileToCpp(p.module):
result = "($5*)(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest] result = "($5*)(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest]
else: else:
result = "($5*)$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest] result = "($5*)$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest]
@ -194,10 +194,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
result = "$1, $1Len_0" % [rdLoc(a)] result = "$1, $1Len_0" % [rdLoc(a)]
of tyString, tySequence: of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst) let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind == tyVar and ntyp.kind == tyString and if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
optSeqDestructors in p.config.globalOptions: optSeqDestructors in p.config.globalOptions:
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
if ntyp.kind == tyVar and not compileToCpp(p.module): if ntyp.kind in {tyVar} and not compileToCpp(p.module):
var t: TLoc var t: TLoc
t.r = "(*$1)" % [a.rdLoc] t.r = "(*$1)" % [a.rdLoc]
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
@ -232,7 +232,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]): elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
initLocExpr(p, n, a) initLocExpr(p, n, a)
result = addrLoc(p.config, a) result = addrLoc(p.config, a)
elif p.module.compileToCpp and param.typ.kind == tyVar and elif p.module.compileToCpp and param.typ.kind in {tyVar} and
n.kind == nkHiddenAddr: n.kind == nkHiddenAddr:
initLocExprSingleUse(p, n[0], a) initLocExprSingleUse(p, n[0], a)
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
@ -372,7 +372,7 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
assert(paramType.kind == nkSym) assert(paramType.kind == nkSym)
if paramType.typ.isCompileTimeOnly: if paramType.typ.isCompileTimeOnly:
result = nil result = nil
elif typ[i].kind == tyVar and ri[i].kind == nkHiddenAddr: elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
result = genArgNoParam(p, ri[i][0]) result = genArgNoParam(p, ri[i][0])
else: else:
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym) result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
@ -449,7 +449,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
var ri = ri[i] var ri = ri[i]
while ri.kind == nkObjDownConv: ri = ri[0] while ri.kind == nkObjDownConv: ri = ri[0]
let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink}) let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink})
if t.kind == tyVar: if t.kind in {tyVar}:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr: if x.typ.kind == tyPtr:
result = genArgNoParam(p, x) result = genArgNoParam(p, x)

View file

@ -126,9 +126,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope =
result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size))) result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size)))
proc genSetNode(p: BProc, n: PNode): Rope = proc genSetNode(p: BProc, n: PNode): Rope =
var cs: TBitSet
var size = int(getSize(p.config, n.typ)) var size = int(getSize(p.config, n.typ))
toBitSet(p.config, n, cs) let cs = toBitSet(p.config, n)
if size > 8: if size > 8:
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
result = p.module.tmpBase & rope(id) result = p.module.tmpBase & rope(id)
@ -676,7 +675,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
proc isCppRef(p: BProc; typ: PType): bool {.inline.} = proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
result = p.module.compileToCpp and result = p.module.compileToCpp and
skipTypes(typ, abstractInstOwned).kind == tyVar and skipTypes(typ, abstractInstOwned).kind in {tyVar} and
tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
proc genDeref(p: BProc, e: PNode, d: var TLoc) = proc genDeref(p: BProc, e: PNode, d: var TLoc) =
@ -693,7 +692,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
typ = typ.lastSon typ = typ.lastSon
typ = typ.skipTypes(abstractInstOwned) typ = typ.skipTypes(abstractInstOwned)
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
initLocExprSingleUse(p, e[0][0], d) initLocExprSingleUse(p, e[0][0], d)
return return
else: else:
@ -716,7 +715,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
else: else:
internalError(p.config, e.info, "genDeref " & $typ.kind) internalError(p.config, e.info, "genDeref " & $typ.kind)
elif p.module.compileToCpp: elif p.module.compileToCpp:
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and
e.kind == nkHiddenDeref: e.kind == nkHiddenDeref:
putIntoDest(p, d, e, rdLoc(a), a.storage) putIntoDest(p, d, e, rdLoc(a), a.storage)
return return
@ -2960,8 +2959,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope =
ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind
case ty case ty
of tySet: of tySet:
var cs: TBitSet let cs = toBitSet(p.config, n)
toBitSet(p.config, n, cs)
result = genRawSetData(cs, int(getSize(p.config, n.typ))) result = genRawSetData(cs, int(getSize(p.config, n.typ)))
of tySequence: of tySequence:
if optSeqDestructors in p.config.globalOptions: if optSeqDestructors in p.config.globalOptions:

View file

@ -701,7 +701,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
return return
case t.kind case t.kind
of tyRef, tyPtr, tyVar, tyLent: of tyRef, tyPtr, tyVar, tyLent:
var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and
compileToCpp(m): "&" else: "*" compileToCpp(m): "&" else: "*"
var et = origTyp.skipTypes(abstractInst).lastSon var et = origTyp.skipTypes(abstractInst).lastSon
var etB = et.skipTypes(abstractInst) var etB = et.skipTypes(abstractInst)

View file

@ -715,10 +715,8 @@ proc genCall(c: var Con; n: PNode) =
for i in 1..<n.len: for i in 1..<n.len:
gen(c, n[i]) gen(c, n[i])
when false: when false:
if t != nil and i < t.len and t[i].kind == tyVar: if t != nil and i < t.len and t[i].kind == tyOut:
# This is wrong! Pass by var is a 'might def', not a 'must def' # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
# like the other defs we emit. This is not good enough for a move
# optimizer.
genDef(c, n[i]) genDef(c, n[i])
# every call can potentially raise: # every call can potentially raise:
if c.inTryStmt > 0 and canRaiseConservative(n[0]): if c.inTryStmt > 0 and canRaiseConservative(n[0]):

View file

@ -46,7 +46,7 @@ proc isLet(n: PNode): bool =
if n.sym.kind in {skLet, skTemp, skForVar}: if n.sym.kind in {skLet, skTemp, skForVar}:
result = true result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ, elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind != tyVar: abstractInst).kind notin {tyVar}:
result = true result = true
proc isVar(n: PNode): bool = proc isVar(n: PNode): bool =

View file

@ -1053,7 +1053,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
lineF(p, "$1 = nimCopy(null, $2, $3);$n", lineF(p, "$1 = nimCopy(null, $2, $3);$n",
[a.rdLoc, b.res, genTypeInfo(p, y.typ)]) [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
of etyObject: of etyObject:
if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: if x.typ.kind in {tyVar} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else: else:
useMagic(p, "nimCopy") useMagic(p, "nimCopy")

View file

@ -113,7 +113,7 @@ const
warnStaticIndexCheck: "$1", warnStaticIndexCheck: "$1",
warnGcUnsafe: "not GC-safe: '$1'", warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1", warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized", warnUninit: "use explicit initialization of '$1' for clarity",
warnGcMem: "'$1' uses GC'ed memory", warnGcMem: "'$1' uses GC'ed memory",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
warnLockLevel: "$1", warnLockLevel: "$1",

View file

@ -59,7 +59,7 @@ proc newFastAsgnStmt*(le, ri: PNode): PNode =
result[0] = le result[0] = le
result[1] = ri result[1] = ri
proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode = proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
result = newNodeI(nkFastAsgn, le.info, 2) result = newNodeI(nkFastAsgn, le.info, 2)
result[0] = le result[0] = le
result[1] = newNodeIT(nkCall, ri.info, ri.typ) result[1] = newNodeIT(nkCall, ri.info, ri.typ)

View file

@ -21,3 +21,7 @@ define:useStdoutAsStdmsg
#define:useNodeIds #define:useNodeIds
#gc:markAndSweep #gc:markAndSweep
@if nimHasWarningObservableStores:
warning[ObservableStores]: off
@end

View file

@ -58,18 +58,18 @@ proc someInSet*(s: PNode, a, b: PNode): bool =
return true return true
result = false result = false
proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) = proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet =
var first, j: Int128 var first, j: Int128
first = firstOrd(conf, s.typ[0]) first = firstOrd(conf, s.typ[0])
bitSetInit(b, int(getSize(conf, s.typ))) bitSetInit(result, int(getSize(conf, s.typ)))
for i in 0..<s.len: for i in 0..<s.len:
if s[i].kind == nkRange: if s[i].kind == nkRange:
j = getOrdValue(s[i][0], first) j = getOrdValue(s[i][0], first)
while j <= getOrdValue(s[i][1], first): while j <= getOrdValue(s[i][1], first):
bitSetIncl(b, toInt64(j - first)) bitSetIncl(result, toInt64(j - first))
inc(j) inc(j)
else: else:
bitSetIncl(b, toInt64(getOrdValue(s[i]) - first)) bitSetIncl(result, toInt64(getOrdValue(s[i]) - first))
proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode = proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode =
var var
@ -106,9 +106,8 @@ proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): P
inc(e) inc(e)
template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} = template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
var x, y: TBitSet var x = toBitSet(conf, a)
toBitSet(conf, a, x) let y = toBitSet(conf, b)
toBitSet(conf, b, y)
op(x, y) op(x, y)
result = toTreeSet(conf, x, a.typ, a.info) result = toTreeSet(conf, x, a.typ, a.info)
@ -118,31 +117,26 @@ proc intersectSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSe
proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff) proc symdiffSets*(conf: ConfigRef; a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
proc containsSets*(conf: ConfigRef; a, b: PNode): bool = proc containsSets*(conf: ConfigRef; a, b: PNode): bool =
var x, y: TBitSet let x = toBitSet(conf, a)
toBitSet(conf, a, x) let y = toBitSet(conf, b)
toBitSet(conf, b, y)
result = bitSetContains(x, y) result = bitSetContains(x, y)
proc equalSets*(conf: ConfigRef; a, b: PNode): bool = proc equalSets*(conf: ConfigRef; a, b: PNode): bool =
var x, y: TBitSet let x = toBitSet(conf, a)
toBitSet(conf, a, x) let y = toBitSet(conf, b)
toBitSet(conf, b, y)
result = bitSetEquals(x, y) result = bitSetEquals(x, y)
proc complement*(conf: ConfigRef; a: PNode): PNode = proc complement*(conf: ConfigRef; a: PNode): PNode =
var x: TBitSet var x = toBitSet(conf, a)
toBitSet(conf, a, x)
for i in 0..high(x): x[i] = not x[i] for i in 0..high(x): x[i] = not x[i]
result = toTreeSet(conf, x, a.typ, a.info) result = toTreeSet(conf, x, a.typ, a.info)
proc deduplicate*(conf: ConfigRef; a: PNode): PNode = proc deduplicate*(conf: ConfigRef; a: PNode): PNode =
var x: TBitSet let x = toBitSet(conf, a)
toBitSet(conf, a, x)
result = toTreeSet(conf, x, a.typ, a.info) result = toTreeSet(conf, x, a.typ, a.info)
proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt = proc cardSet*(conf: ConfigRef; a: PNode): BiggestInt =
var x: TBitSet let x = toBitSet(conf, a)
toBitSet(conf, a, x)
result = bitSetCard(x) result = bitSetCard(x)
proc setHasRange*(s: PNode): bool = proc setHasRange*(s: PNode): bool =

View file

@ -214,7 +214,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arNone result = arNone
case n.kind case n.kind
of nkEmpty: of nkEmpty:
if n.typ != nil and n.typ.kind == tyVar: if n.typ != nil and n.typ.kind in {tyVar}:
result = arLValue result = arLValue
of nkSym: of nkSym:
let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar} let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar}
@ -231,7 +231,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arLValue result = arLValue
elif n.sym.kind == skType: elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc}) let t = n.sym.typ.skipTypes({tyTypeDesc})
if t.kind == tyVar: result = arStrange if t.kind in {tyVar}: result = arStrange
of nkDotExpr: of nkDotExpr:
let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
if t.kind in {tyVar, tySink, tyPtr, tyRef}: if t.kind in {tyVar, tySink, tyPtr, tyRef}:
@ -277,7 +277,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
# builtin slice keeps lvalue-ness: # builtin slice keeps lvalue-ness:
if getMagic(n) in {mArrGet, mSlice}: if getMagic(n) in {mArrGet, mSlice}:
result = isAssignable(owner, n[1], isUnsafeAddr) result = isAssignable(owner, n[1], isUnsafeAddr)
elif n.typ != nil and n.typ.kind == tyVar: elif n.typ != nil and n.typ.kind in {tyVar}:
result = arLValue result = arLValue
elif isUnsafeAddr and n.typ != nil and n.typ.kind == tyLent: elif isUnsafeAddr and n.typ != nil and n.typ.kind == tyLent:
result = arLValue result = arLValue

View file

@ -793,7 +793,7 @@ proc primarySuffix(p: var TParser, r: PNode,
break break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
tkOpr, tkDotDot, tkVar, tkStatic, tkType, tkEnum, tkTuple, tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple,
tkObject, tkProc: tkObject, tkProc:
# XXX: In type sections we allow the free application of the # XXX: In type sections we allow the free application of the
# command syntax, with the exception of expressions such as # command syntax, with the exception of expressions such as
@ -1300,6 +1300,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
optInd(p, result) optInd(p, result)
result.add(primary(p, pmNormal)) result.add(primary(p, pmNormal))
of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode) of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
of tkOut:
# I like this parser extension to be in 1.4 as it still might turn out
# useful in the long run.
result = parseTypeDescKAux(p, nkMutableTy, mode)
of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode) of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode) of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode) of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)

View file

@ -309,8 +309,8 @@ proc lsub(g: TSrcGen; n: PNode): int
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc skip(t: PType): PType = proc skip(t: PType): PType =
result = t result = t
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct, while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
tyOrdinal, tyAlias, tySink}: tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
result = lastSon(result) result = lastSon(result)
let typ = n.typ.skip let typ = n.typ.skip
@ -883,7 +883,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
case n.kind case n.kind
of nkClosedSymChoice, nkOpenSymChoice: of nkClosedSymChoice, nkOpenSymChoice:
if n.len > 0: result = bracketKind(g, n[0]) if n.len > 0: result = bracketKind(g, n[0])
of nkSym: of nkSym:
result = case n.sym.name.s result = case n.sym.name.s
of "[]": bkBracket of "[]": bkBracket
of "[]=": bkBracketAsgn of "[]=": bkBracketAsgn
@ -974,7 +974,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParRi, ")") put(g, tkParRi, ")")
put(g, tkColon, ":") put(g, tkColon, ":")
gsub(g, n, n.len-1) gsub(g, n, n.len-1)
elif n.len >= 1: elif n.len >= 1:
case bracketKind(g, n[0]) case bracketKind(g, n[0])
of bkBracket: of bkBracket:
gsub(g, n, 1) gsub(g, n, 1)

View file

@ -107,6 +107,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string =
var indent = info.col var indent = info.col
let numLines = numLines(conf, info.fileIndex).uint16 let numLines = numLines(conf, info.fileIndex).uint16
var lastNonemptyPos = 0 var lastNonemptyPos = 0
result = ""
var ldata = LineData(lineFirst: first.line.int, conf: conf) var ldata = LineData(lineFirst: first.line.int, conf: conf)
visitMultilineStrings(ldata, n[^1]) visitMultilineStrings(ldata, n[^1])

View file

@ -676,7 +676,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
var x: PType var x: PType
if param.typ.kind == tyVar: if param.typ.kind == tyVar:
x = newTypeS(tyVar, c) x = newTypeS(param.typ.kind, c)
x.addSonSkipIntLit t.baseOfDistinct x.addSonSkipIntLit t.baseOfDistinct
else: else:
x = t.baseOfDistinct x = t.baseOfDistinct

View file

@ -712,7 +712,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# note sometimes this is eval'ed twice so we check for nkHiddenAddr here: # note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
for i in 1..<n.len: for i in 1..<n.len:
if i < t.len and t[i] != nil and if i < t.len and t[i] != nil and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind == tyVar: skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
let it = n[i] let it = n[i]
if isAssignable(c, it) notin {arLValue, arLocalLValue}: if isAssignable(c, it) notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr: if it.kind != nkHiddenAddr:
@ -733,7 +733,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# calls and then they wouldn't be analysed otherwise # calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n[i]) analyseIfAddressTakenInCall(c, n[i])
if i < t.len and if i < t.len and
skipTypes(t[i], abstractInst-{tyTypeDesc}).kind == tyVar: skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
if n[i].kind != nkHiddenAddr: if n[i].kind != nkHiddenAddr:
n[i] = analyseIfAddressTaken(c, n[i]) n[i] = analyseIfAddressTaken(c, n[i])
@ -1711,7 +1711,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
var le = a.typ var le = a.typ
if le == nil: if le == nil:
localError(c.config, a.info, "expression has no type") localError(c.config, a.info, "expression has no type")
elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and
isAssignable(c, a) in {arNone, arLentValue}) or isAssignable(c, a) in {arNone, arLentValue}) or
skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
# Direct assignment to a discriminant is allowed! # Direct assignment to a discriminant is allowed!

View file

@ -42,9 +42,6 @@ type
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode
proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode
proc skipAddr(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n[0] else: n)
proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
result = newNodeI(nkBracketExpr, n.info) result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i]) for i in 1..<n.len: result.add(n[i])

View file

@ -249,7 +249,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
else: else:
badDiscriminatorError() badDiscriminatorError()
elif discriminatorVal.sym.kind notin {skLet, skParam} or elif discriminatorVal.sym.kind notin {skLet, skParam} or
discriminatorVal.sym.typ.kind == tyVar: discriminatorVal.sym.typ.kind in {tyVar}:
localError(c.config, discriminatorVal.info, localError(c.config, discriminatorVal.info,
"runtime discriminator must be immutable if branch fields are " & "runtime discriminator must be immutable if branch fields are " &
"initialized, a 'let' binding is required.") "initialized, a 'let' binding is required.")

View file

@ -66,7 +66,7 @@ type
TEffects = object TEffects = object
exc: PNode # stack of exceptions exc: PNode # stack of exceptions
tags: PNode # list of tags tags: PNode # list of tags
bottom, inTryStmt, inExceptOrFinallyStmt: int bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn: int
owner: PSym owner: PSym
ownerModule: PSym ownerModule: PSym
init: seq[int] # list of initialized variables init: seq[int] # list of initialized variables
@ -87,6 +87,7 @@ proc `==`(a, b: TLockLevel): bool {.borrow.}
proc max(a, b: TLockLevel): TLockLevel {.borrow.} proc max(a, b: TLockLevel): TLockLevel {.borrow.}
proc isLocalVar(a: PEffects, s: PSym): bool = proc isLocalVar(a: PEffects, s: PSym): bool =
# and (s.kind != skParam or s.typ.kind == tyOut)
s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.kind in {skVar, skResult} and sfGlobal notin s.flags and
s.owner == a.owner and s.typ != nil s.owner == a.owner and s.typ != nil
@ -246,6 +247,18 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
var cycleCheck = initIntSet() var cycleCheck = initIntSet()
listGcUnsafety(s, onlyWarning, cycleCheck, conf) listGcUnsafety(s, onlyWarning, cycleCheck, conf)
proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) =
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
s.magic != mNimvm:
if s.guard != nil: guardGlobal(a, n, s.guard)
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n)
markGcUnsafe(a, s)
markSideEffect(a, s)
else:
markSideEffect(a, s)
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
if a.inExceptOrFinallyStmt > 0: if a.inExceptOrFinallyStmt > 0:
@ -257,20 +270,11 @@ proc useVar(a: PEffects, n: PNode) =
elif s.id notin a.init: elif s.id notin a.init:
if s.typ.requiresInit: if s.typ.requiresInit:
message(a.config, n.info, warnProveInit, s.name.s) message(a.config, n.info, warnProveInit, s.name.s)
else: elif a.leftPartOfAsgn <= 0:
message(a.config, n.info, warnUninit, s.name.s) message(a.config, n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable: # prevent superfluous warnings about the same variable:
a.init.add s.id a.init.add s.id
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and useVarNoInitCheck(a, n, s)
s.magic != mNimvm:
if s.guard != nil: guardGlobal(a, n, s.guard)
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
#if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n)
markGcUnsafe(a, s)
markSideEffect(a, s)
else:
markSideEffect(a, s)
type type
@ -538,7 +542,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
proc isTrival(caller: PNode): bool {.inline.} = proc isTrival(caller: PNode): bool {.inline.} =
result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved} result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved}
proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) =
let a = skipConvAndClosure(n) let a = skipConvAndClosure(n)
let op = a.typ let op = a.typ
# assume indirect calls are taken here: # assume indirect calls are taken here:
@ -572,7 +576,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode)
markGcUnsafe(tracked, a) markGcUnsafe(tracked, a)
elif tfNoSideEffect notin op.flags: elif tfNoSideEffect notin op.flags:
markSideEffect(tracked, a) markSideEffect(tracked, a)
if paramType != nil and paramType.kind == tyVar: if paramType != nil and paramType.kind in {tyVar}:
invalidateFacts(tracked.guards, n) invalidateFacts(tracked.guards, n)
if n.kind == nkSym and isLocalVar(tracked, n.sym): if n.kind == nkSym and isLocalVar(tracked, n.sym):
makeVolatile(tracked, n.sym) makeVolatile(tracked, n.sym)
@ -732,7 +736,7 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
optSeqDestructors in tracked.config.globalOptions: optSeqDestructors in tracked.config.globalOptions:
tracked.owner.flags.incl sfInjectDestructors tracked.owner.flags.incl sfInjectDestructors
proc track(tracked: PEffects, n: PNode) = proc trackCall(tracked: PEffects; n: PNode) =
template gcsafeAndSideeffectCheck() = template gcsafeAndSideeffectCheck() =
if notGcSafe(op) and not importedFromC(a): if notGcSafe(op) and not importedFromC(a):
# and it's not a recursive call: # and it's not a recursive call:
@ -744,11 +748,105 @@ proc track(tracked: PEffects, n: PNode) =
if not (a.kind == nkSym and a.sym == tracked.owner): if not (a.kind == nkSym and a.sym == tracked.owner):
markSideEffect(tracked, a) markSideEffect(tracked, a)
# p's effects are ours too:
var a = n[0]
#if canRaise(a):
# echo "this can raise ", tracked.config $ n.info
let op = a.typ
if n.typ != nil:
if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
createTypeBoundOps(tracked, n.typ, n.info)
if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil:
return
if a.kind == nkCast and a[1].typ.kind == tyProc:
a = a[1]
# XXX: in rare situations, templates and macros will reach here after
# calling getAst(templateOrMacro()). Currently, templates and macros
# are indistinguishable from normal procs (both have tyProc type) and
# we can detect them only by checking for attached nkEffectList.
if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
if a.kind == nkSym:
if a.sym == tracked.owner: tracked.isRecursive = true
# even for recursive calls we need to check the lock levels (!):
mergeLockLevels(tracked, n, a.sym.getLockLevel)
if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
else:
mergeLockLevels(tracked, n, op.lockLevel)
var effectList = op.n[0]
if a.kind == nkSym and a.sym.kind == skMethod:
propagateEffects(tracked, n, a.sym)
elif isNoEffectList(effectList):
if isForwardedProc(a):
propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner):
assumeTheWorst(tracked, n, op)
gcsafeAndSideeffectCheck()
else:
mergeEffects(tracked, effectList[exceptionEffects], n)
mergeTags(tracked, effectList[tagEffects], n)
gcsafeAndSideeffectCheck()
if a.kind != nkSym or a.sym.magic != mNBindSym:
for i in 1..<n.len: trackOperandForIndirectCall(tracked, n[i], paramType(op, i), a)
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is:
let arg = n[1]
initVarViaNew(tracked, arg)
if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
n[2].intVal == 0:
# var s: seq[notnil]; newSeq(s, 0) is a special case!
discard
else:
message(tracked.config, arg.info, warnProveInit, $arg)
# check required for 'nim check':
if n[1].typ.len > 0:
createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
createTypeBoundOps(tracked, n[1].typ, n.info)
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and
optStaticBoundsCheck in tracked.currOptions:
checkBounds(tracked, n[1], n[2])
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro:
let opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]:
createTypeBoundOps(tracked, t, n.info)
let op = t.attachedOps[TTypeAttachedOp(opKind)]
if op != nil:
n[0].sym = op
if a.kind != nkSym or a.sym.magic != mRunnableExamples:
for i in 0..<n.safeLen:
track(tracked, n[i])
if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.len):
case op[i].kind
of tySink:
checkForSink(tracked.config, tracked.owner, n[i])
#of tyOut:
# consider this case: p(out x, x); we want to remark that 'x' is not
# initialized until after the call. Since we do this after we analysed the
# call, this is fine.
# initVar(tracked, n[i].skipAddr, false)
else: discard
proc track(tracked: PEffects, n: PNode) =
case n.kind case n.kind
of nkSym: of nkSym:
useVar(tracked, n) useVar(tracked, n)
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags: if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
tracked.owner.flags.incl sfInjectDestructors tracked.owner.flags.incl sfInjectDestructors
of nkHiddenAddr, nkAddr:
if n[0].kind == nkSym and isLocalVar(tracked, n[0].sym):
useVarNoInitCheck(tracked, n[0], n[0].sym)
else:
track(tracked, n[0])
of nkRaiseStmt: of nkRaiseStmt:
if n[0].kind != nkEmpty: if n[0].kind != nkEmpty:
n[0].info = n.info n[0].info = n.info
@ -763,86 +861,7 @@ proc track(tracked: PEffects, n: PNode) =
# Here we add a `Exception` tag in order to cover both the cases. # Here we add a `Exception` tag in order to cover both the cases.
addEffect(tracked, createRaise(tracked.graph, n), nil) addEffect(tracked, createRaise(tracked.graph, n), nil)
of nkCallKinds: of nkCallKinds:
# p's effects are ours too: trackCall(tracked, n)
var a = n[0]
#if canRaise(a):
# echo "this can raise ", tracked.config $ n.info
let op = a.typ
if n.typ != nil:
if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
createTypeBoundOps(tracked, n.typ, n.info)
if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil:
return
if a.kind == nkCast and a[1].typ.kind == tyProc:
a = a[1]
# XXX: in rare situations, templates and macros will reach here after
# calling getAst(templateOrMacro()). Currently, templates and macros
# are indistinguishable from normal procs (both have tyProc type) and
# we can detect them only by checking for attached nkEffectList.
if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList:
if a.kind == nkSym:
if a.sym == tracked.owner: tracked.isRecursive = true
# even for recursive calls we need to check the lock levels (!):
mergeLockLevels(tracked, n, a.sym.getLockLevel)
if sfSideEffect in a.sym.flags: markSideEffect(tracked, a)
else:
mergeLockLevels(tracked, n, op.lockLevel)
var effectList = op.n[0]
if a.kind == nkSym and a.sym.kind == skMethod:
propagateEffects(tracked, n, a.sym)
elif isNoEffectList(effectList):
if isForwardedProc(a):
propagateEffects(tracked, n, a.sym)
elif isIndirectCall(a, tracked.owner):
assumeTheWorst(tracked, n, op)
gcsafeAndSideeffectCheck()
else:
mergeEffects(tracked, effectList[exceptionEffects], n)
mergeTags(tracked, effectList[tagEffects], n)
gcsafeAndSideeffectCheck()
if a.kind != nkSym or a.sym.magic != mNBindSym:
for i in 1..<n.len: trackOperand(tracked, n[i], paramType(op, i), a)
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is:
let arg = n[1]
initVarViaNew(tracked, arg)
if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
n[2].intVal == 0:
# var s: seq[notnil]; newSeq(s, 0) is a special case!
discard
else:
message(tracked.config, arg.info, warnProveInit, $arg)
# check required for 'nim check':
if n[1].typ.len > 0:
createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
createTypeBoundOps(tracked, n[1].typ, n.info)
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and
optStaticBoundsCheck in tracked.currOptions:
checkBounds(tracked, n[1], n[2])
if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro:
let opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]:
createTypeBoundOps(tracked, t, n.info)
let op = t.attachedOps[TTypeAttachedOp(opKind)]
if op != nil:
n[0].sym = op
if a.kind != nkSym or a.sym.magic != mRunnableExamples:
for i in 0..<n.safeLen:
track(tracked, n[i])
if op != nil and op.kind == tyProc:
for i in 1..<min(n.safeLen, op.len):
if op[i].kind == tySink:
checkForSink(tracked.config, tracked.owner, n[i])
of nkDotExpr: of nkDotExpr:
guardDotAccess(tracked, n) guardDotAccess(tracked, n)
for i in 0..<n.len: track(tracked, n[i]) for i in 0..<n.len: track(tracked, n[i])
@ -856,7 +875,9 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, n[1]) track(tracked, n[1])
initVar(tracked, n[0], volatileCheck=true) initVar(tracked, n[0], volatileCheck=true)
invalidateFacts(tracked.guards, n[0]) invalidateFacts(tracked.guards, n[0])
inc tracked.leftPartOfAsgn
track(tracked, n[0]) track(tracked, n[0])
dec tracked.leftPartOfAsgn
addAsgnFact(tracked.guards, n[0], n[1]) addAsgnFact(tracked.guards, n[0], n[1])
notNilCheck(tracked, n[1], n[0].typ) notNilCheck(tracked, n[1], n[0].typ)
when false: cstringCheck(tracked, n) when false: cstringCheck(tracked, n)
@ -1042,7 +1063,10 @@ proc track(tracked: PEffects, n: PNode) =
if optStaticBoundsCheck in tracked.currOptions and n.len == 2: if optStaticBoundsCheck in tracked.currOptions and n.len == 2:
if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple: if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
checkBounds(tracked, n[0], n[1]) checkBounds(tracked, n[0], n[1])
for i in 0 ..< n.len: track(tracked, n[i]) track(tracked, n[0])
dec tracked.leftPartOfAsgn
for i in 1 ..< n.len: track(tracked, n[i])
inc tracked.leftPartOfAsgn
else: else:
for i in 0..<n.safeLen: track(tracked, n[i]) for i in 0..<n.safeLen: track(tracked, n[i])
@ -1180,6 +1204,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
if isSinkTypeForParam(typ) or if isSinkTypeForParam(typ) or
(t.config.selectedGC in {gcArc, gcOrc} and isClosure(typ.skipTypes(abstractInst))): (t.config.selectedGC in {gcArc, gcOrc} and isClosure(typ.skipTypes(abstractInst))):
createTypeBoundOps(t, typ, param.info) createTypeBoundOps(t, typ, param.info)
when false:
if typ.kind == tyOut and param.id notin t.init:
message(g.config, param.info, warnProveInit, param.name.s)
if not isEmptyType(s.typ[0]) and if not isEmptyType(s.typ[0]) and
(s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar) and (s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar) and

View file

@ -731,13 +731,8 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
var v = symForVar(c, n[0][i]) var v = symForVar(c, n[0][i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
case iter.kind case iter.kind
of tyVar: of tyVar, tyLent:
v.typ = newTypeS(tyVar, c) v.typ = newTypeS(iter.kind, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
of tyLent:
v.typ = newTypeS(tyLent, c)
v.typ.add iterAfterVarLent[i] v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags: if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr v.typ.flags.incl tfVarIsPtr
@ -794,13 +789,8 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
var v = symForVar(c, n[i]) var v = symForVar(c, n[i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
case iter.kind case iter.kind
of tyVar: of tyVar, tyLent:
v.typ = newTypeS(tyVar, c) v.typ = newTypeS(iter.kind, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
of tyLent:
v.typ = newTypeS(tyLent, c)
v.typ.add iterAfterVarLent[i] v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags: if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr v.typ.flags.incl tfVarIsPtr

View file

@ -193,16 +193,16 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, n.info, errXExpectsOneTypeParam % "varargs") localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
addSonSkipIntLit(result, errorType(c)) addSonSkipIntLit(result, errorType(c))
proc semVarType(c: PContext, n: PNode, prev: PType): PType = proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
if n.len == 1: if n.len == 1:
result = newOrPrevType(tyVar, prev, c) result = newOrPrevType(kind, prev, c)
var base = semTypeNode(c, n[0], nil).skipTypes({tyTypeDesc}) var base = semTypeNode(c, n[0], nil).skipTypes({tyTypeDesc})
if base.kind == tyVar: if base.kind == tyVar:
localError(c.config, n.info, "type 'var var' is not allowed") localError(c.config, n.info, "type 'var var' is not allowed")
base = base[0] base = base[0]
addSonSkipIntLit(result, base) addSonSkipIntLit(result, base)
else: else:
result = newConstraint(c, tyVar) result = newConstraint(c, kind)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType = proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
if n.len == 0: return newConstraint(c, tyDistinct) if n.len == 0: return newConstraint(c, tyDistinct)
@ -1915,7 +1915,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTypeClassTy: result = semTypeClass(c, n, prev) of nkTypeClassTy: result = semTypeClass(c, n, prev)
of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
of nkVarTy: result = semVarType(c, n, prev) of nkVarTy: result = semVarOutType(c, n, prev, tyVar)
of nkDistinctTy: result = semDistinct(c, n, prev) of nkDistinctTy: result = semDistinct(c, n, prev)
of nkStaticTy: result = semStaticType(c, n[0], prev) of nkStaticTy: result = semStaticType(c, n[0], prev)
of nkIteratorTy: of nkIteratorTy:

View file

@ -1902,7 +1902,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
if destIsGeneric: if destIsGeneric:
dest = generateTypeInstance(c, m.bindings, arg, dest) dest = generateTypeInstance(c, m.bindings, arg, dest)
let fdest = typeRel(m, f, dest) let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind == tyVar): if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind in {tyVar}):
markUsed(c, arg.info, c.converters[i]) markUsed(c, arg.info, c.converters[i])
var s = newSymNode(c.converters[i]) var s = newSymNode(c.converters[i])
s.typ = c.converters[i].typ s.typ = c.converters[i].typ
@ -1915,7 +1915,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
var param: PNode = nil var param: PNode = nil
if srca == isSubtype: if srca == isSubtype:
param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c) param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
elif src.kind == tyVar: elif src.kind in {tyVar}:
# Analyse the converter return type # Analyse the converter return type
param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1]) param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1])
param.add copyTree(arg) param.add copyTree(arg)
@ -2082,7 +2082,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
result = implicitConv(nkHiddenSubConv, f, arg, m, c) result = implicitConv(nkHiddenSubConv, f, arg, m, c)
of isSubrange: of isSubrange:
inc(m.subtypeMatches) inc(m.subtypeMatches)
if f.kind == tyVar: if f.kind in {tyVar}:
result = arg result = arg
else: else:
result = implicitConv(nkHiddenStdConv, f, arg, m, c) result = implicitConv(nkHiddenStdConv, f, arg, m, c)
@ -2327,10 +2327,10 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
noMatch() noMatch()
return return
if formal.typ.kind == tyVar: if formal.typ.kind in {tyVar}:
let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
if argConverter.kind == nkHiddenCallConv: if argConverter.kind == nkHiddenCallConv:
if argConverter.typ.kind != tyVar: if argConverter.typ.kind notin {tyVar}:
m.firstMismatch.kind = kVarNeeded m.firstMismatch.kind = kVarNeeded
noMatch() noMatch()
return return
@ -2604,7 +2604,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
if op == attachedDeepCopy: if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon if f.kind in {tyRef, tyPtr}: f = f.lastSon
else: else:
if f.kind == tyVar: f = f.lastSon if f.kind in {tyVar}: f = f.lastSon
if typeRel(m, f, t) == isNone: if typeRel(m, f, t) == isNone:
localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
else: else:

View file

@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
else: srFlowVar else: srFlowVar
proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind = proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind =
if c.selectedGC in {gcArc, gcOrc}: fvBlob if c.selectedGC in {gcArc, gcOrc}: fvBlob
elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
elif containsGarbageCollectedRef(t): fvInvalid elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob else: fvBlob
@ -201,7 +201,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
# we pick n's type here, which hopefully is 'tyArray' and not # we pick n's type here, which hopefully is 'tyArray' and not
# 'tyOpenArray': # 'tyOpenArray':
var argType = n[i].typ.skipTypes(abstractInst) var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len: if i < formals.len:
if formals[i].typ.kind in {tyVar, tyLent}: if formals[i].typ.kind in {tyVar, tyLent}:
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter") localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
if formals[i].typ.kind in {tyTypeDesc, tyStatic}: if formals[i].typ.kind in {tyTypeDesc, tyStatic}:

View file

@ -423,7 +423,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
if n.typ.skipTypes(abstractVar).kind != tyOpenArray: if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}: elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ) result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var m = n[0][1] var m = n[0][1]
if m.kind == a or m.kind == b: if m.kind == a or m.kind == b:
@ -433,7 +433,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
if n.typ.skipTypes(abstractVar).kind != tyOpenArray: if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
result.typ = n.typ result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}: elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ) result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
else: else:
if n[0].kind == a or n[0].kind == b: if n[0].kind == a or n[0].kind == b:
# addr ( deref ( x )) --> x # addr ( deref ( x )) --> x
@ -569,14 +569,14 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
result = putArgInto(arg[0], formal) result = putArgInto(arg[0], formal)
of nkCurly, nkBracket: of nkCurly, nkBracket:
for i in 0..<arg.len: for i in 0..<arg.len:
if putArgInto(arg[i], formal) != paDirectMapping: if putArgInto(arg[i], formal) != paDirectMapping:
return paFastAsgn return paFastAsgn
result = paDirectMapping result = paDirectMapping
of nkPar, nkTupleConstr, nkObjConstr: of nkPar, nkTupleConstr, nkObjConstr:
for i in 0..<arg.len: for i in 0..<arg.len:
let a = if arg[i].kind == nkExprColonExpr: arg[i][1] let a = if arg[i].kind == nkExprColonExpr: arg[i][1]
else: arg[0] else: arg[0]
if putArgInto(a, formal) != paDirectMapping: if putArgInto(a, formal) != paDirectMapping:
return paFastAsgn return paFastAsgn
result = paDirectMapping result = paDirectMapping
else: else:
@ -667,7 +667,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg)) stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
idNodeTablePut(newC.mapping, formal, temp) idNodeTablePut(newC.mapping, formal, temp)
of paVarAsgn: of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind == tyVar) assert(skipTypes(formal.typ, abstractInst).kind in {tyVar})
idNodeTablePut(newC.mapping, formal, arg) idNodeTablePut(newC.mapping, formal, arg)
# XXX BUG still not correct if the arg has a side effect! # XXX BUG still not correct if the arg has a side effect!
of paComplexOpenarray: of paComplexOpenarray:

View file

@ -438,7 +438,7 @@ const
"lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type", "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass", "BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred", "UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor", "and", "or", "not", "any", "static", "TypeFromExpr", "out ",
"void"] "void"]
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,

View file

@ -85,7 +85,7 @@ proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) =
assert(typ.n[i].kind == nkSym) assert(typ.n[i].kind == nkSym)
let paramType = typ.n[i] let paramType = typ.n[i]
if paramType.typ.isCompileTimeOnly: continue if paramType.typ.isCompileTimeOnly: continue
if sfEscapes in paramType.sym.flags or paramType.typ.kind == tyVar: if sfEscapes in paramType.sym.flags or paramType.typ.kind in {tyVar}:
allRoots(it, result, info) allRoots(it, result, info)
else: else:
allRoots(it, result, info) allRoots(it, result, info)
@ -164,7 +164,7 @@ proc depsArgs(w: var W; n: PNode) =
let paramType = typ.n[i] let paramType = typ.n[i]
if paramType.typ.isCompileTimeOnly: continue if paramType.typ.isCompileTimeOnly: continue
var destInfo: set[RootInfo] = {} var destInfo: set[RootInfo] = {}
if sfWrittenTo in paramType.sym.flags or paramType.typ.kind == tyVar: if sfWrittenTo in paramType.sym.flags or paramType.typ.kind in {tyVar}:
# p(f(x, y), X, g(h, z)) # p(f(x, y), X, g(h, z))
destInfo.incl markAsWrittenTo destInfo.incl markAsWrittenTo
if sfEscapes in paramType.sym.flags: if sfEscapes in paramType.sym.flags:
@ -247,7 +247,7 @@ proc markWriteOrEscape(w: var W; conf: ConfigRef) =
for p in a.dest: for p in a.dest:
if p.kind == skParam and p.owner == w.owner: if p.kind == skParam and p.owner == w.owner:
incl(p.flags, sfWrittenTo) incl(p.flags, sfWrittenTo)
if w.owner.kind == skFunc and p.typ.kind != tyVar: if w.owner.kind == skFunc and p.typ.kind notin {tyVar}:
localError(conf, a.info, "write access to non-var parameter: " & p.name.s) localError(conf, a.info, "write access to non-var parameter: " & p.name.s)
if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}: if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:

View file

@ -2458,12 +2458,13 @@ matches) is preferred:
gen(ri) # "ref T" gen(ri) # "ref T"
Overloading based on 'var T' Overloading based on 'var T' / 'out T'
---------------------------- --------------------------------------
If the formal parameter ``f`` is of type ``var T`` in addition to the ordinary If the formal parameter ``f`` is of type ``var T`` (or ``out T``)
type checking, the argument is checked to be an `l-value`:idx:. ``var T`` in addition to the ordinary
matches better than just ``T`` then. type checking, the argument is checked to be an `l-value`:idx:.
``var T`` (or ``out T``) matches better than just ``T`` then.
.. code-block:: nim .. code-block:: nim
proc sayHi(x: int): string = proc sayHi(x: int): string =
@ -2482,6 +2483,17 @@ matches better than just ``T`` then.
# 13 # 13
An l-value matches ``var T`` and ``out T`` equally well, hence
the following is ambiguous:
.. code-block:: nim
proc p(x: out string) = x = ""
proc p(x: var string) = x = ""
var v: string
p(v) # ambiguous
Lazy type resolution for untyped Lazy type resolution for untyped
-------------------------------- --------------------------------
@ -4230,9 +4242,7 @@ error message from ``e``, and for such situations it is enough to use
Custom exceptions Custom exceptions
----------------- -----------------
Is it possible to create custom exceptions. These make it easy to distinguish between exceptions raised by nim and those from your own code. Is it possible to create custom exceptions. A custom exception is a custom type:
A custom exception is a custom type:
.. code-block:: nim .. code-block:: nim
type type
@ -4782,7 +4792,7 @@ of "typedesc"-ness is stripped off:
Generic inference restrictions Generic inference restrictions
------------------------------ ------------------------------
The types ``var T`` and ``typedesc[T]`` cannot be inferred in a generic The types ``var T``, ``out T`` and ``typedesc[T]`` cannot be inferred in a generic
instantiation. The following is not allowed: instantiation. The following is not allowed:
.. code-block:: nim .. code-block:: nim
@ -5930,7 +5940,7 @@ noSideEffect pragma
The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side
effects. This means that the proc/iterator only changes locations that are effects. This means that the proc/iterator only changes locations that are
reachable from its parameters and the return value only depends on the reachable from its parameters and the return value only depends on the
arguments. If none of its parameters have the type ``var T`` arguments. If none of its parameters have the type ``var T`` or ``out T``
or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static
error to mark a proc/iterator to have no side effect if the compiler cannot error to mark a proc/iterator to have no side effect if the compiler cannot
verify this. verify this.
@ -6593,7 +6603,7 @@ during semantic analysis:
Emit pragma Emit pragma
----------- -----------
The ``emit`` pragma can be used to directly affect the output of the The ``emit`` pragma can be used to directly affect the output of the
compiler's code generator. So it makes your code unportable to other code compiler's code generator. The code is then unportable to other code
generators/backends. Its usage is highly discouraged! However, it can be generators/backends. Its usage is highly discouraged! However, it can be
extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code. extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code.

View file

@ -482,7 +482,7 @@ nil`` annotation to exclude ``nil`` as a valid value:
.. code-block:: nim .. code-block:: nim
{.experimental: "notnil"} {.experimental: "notnil"}
type type
PObject = ref TObj not nil PObject = ref TObj not nil
TProc = (proc (x, y: int)) not nil TProc = (proc (x, y: int)) not nil
@ -1828,8 +1828,8 @@ Aliasing restrictions in parameter passing
implementation and need to be fleshed out further. implementation and need to be fleshed out further.
"Aliasing" here means that the underlying storage locations overlap in memory "Aliasing" here means that the underlying storage locations overlap in memory
at runtime. An "output parameter" is a parameter of type ``var T``, an input at runtime. An "output parameter" is a parameter of type ``var T``,
parameter is any parameter that is not of type ``var``. an input parameter is any parameter that is not of type ``var``.
1. Two output parameters should never be aliased. 1. Two output parameters should never be aliased.
2. An input and an output parameter should not be aliased. 2. An input and an output parameter should not be aliased.

View file

@ -535,9 +535,7 @@ proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffe
proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
## Returns ``LineInfo`` of ``n``, using absolute path for ``filename``. ## Returns ``LineInfo`` of ``n``, using absolute path for ``filename``.
result.filename = n.getFile result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn)
result.line = n.getLine
result.column = n.getColumn
proc lineInfo*(arg: NimNode): string {.compileTime.} = proc lineInfo*(arg: NimNode): string {.compileTime.} =
## Return line info in the form `filepath(line, column)`. ## Return line info in the form `filepath(line, column)`.
@ -878,12 +876,14 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} =
## Convert the AST `n` to a human-readable tree-like string. ## Convert the AST `n` to a human-readable tree-like string.
## ##
## See also `repr`, `lispRepr`, and `astGenRepr`. ## See also `repr`, `lispRepr`, and `astGenRepr`.
result = ""
n.treeTraverse(result, isLisp = false, indented = true) n.treeTraverse(result, isLisp = false, indented = true)
proc lispRepr*(n: NimNode; indented = false): string {.compileTime, benign.} = proc lispRepr*(n: NimNode; indented = false): string {.compileTime, benign.} =
## Convert the AST ``n`` to a human-readable lisp-like string. ## Convert the AST ``n`` to a human-readable lisp-like string.
## ##
## See also ``repr``, ``treeRepr``, and ``astGenRepr``. ## See also ``repr``, ``treeRepr``, and ``astGenRepr``.
result = ""
n.treeTraverse(result, isLisp = true, indented = indented) n.treeTraverse(result, isLisp = true, indented = indented)
proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
@ -1611,6 +1611,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf") ## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
## assert(o.getCustomPragmaVal(serializationKey) == "mo") ## assert(o.getCustomPragmaVal(serializationKey) == "mo")
## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo") ## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
result = nil
let pragmaNode = customPragmaNode(n) let pragmaNode = customPragmaNode(n)
for p in pragmaNode: for p in pragmaNode:
if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp: if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:

View file

@ -187,6 +187,7 @@ proc binarySearch*[T, K](a: openArray[T], key: K,
else: else:
return -1 return -1
result = 0
if (len and (len - 1)) == 0: if (len and (len - 1)) == 0:
# when `len` is a power of 2, a faster shr can be used. # when `len` is a power of 2, a faster shr can be used.
var step = len shr 1 var step = len shr 1

View file

@ -395,6 +395,8 @@ proc firstSetBitNim(x: uint64): int {.inline, noSideEffect.} =
if k == 0: if k == 0:
k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32 k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32
result = 32 result = 32
else:
result = 0
result += firstSetBitNim(k) result += firstSetBitNim(k)
proc fastlog2Nim(x: uint32): int {.inline, noSideEffect.} = proc fastlog2Nim(x: uint32): int {.inline, noSideEffect.} =

View file

@ -417,7 +417,8 @@ proc relativePath*(path, base: string, sep = DirSep): string {.
if not sameRoot(path, base): if not sameRoot(path, base):
return path return path
var f, b: PathIter var f = default PathIter
var b = default PathIter
var ff = (0, -1) var ff = (0, -1)
var bb = (0, -1) # (int, int) var bb = (0, -1) # (int, int)
result = newStringOfCap(path.len) result = newStringOfCap(path.len)
@ -1053,6 +1054,7 @@ when defined(windows) or defined(posix) or defined(nintendoswitch):
assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\"" assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\""
# can't use `map` pending https://github.com/nim-lang/Nim/issues/8303 # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
result = ""
for i in 0..<args.len: for i in 0..<args.len:
if i > 0: result.add " " if i > 0: result.add " "
result.add quoteShell(args[i]) result.add quoteShell(args[i])
@ -2981,7 +2983,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW
setLen(result, L) setLen(result, L)
break break
elif defined(macosx): elif defined(macosx):
var size: cuint32 var size = cuint32(0)
getExecPath1(nil, size) getExecPath1(nil, size)
result = newString(int(size)) result = newString(int(size))
if getExecPath2(result, size): if getExecPath2(result, size):

View file

@ -249,6 +249,7 @@ proc skipWhitespace*(s: string, start = 0): int {.inline.} =
doAssert skipWhitespace(" Hello World", 0) == 1 doAssert skipWhitespace(" Hello World", 0) == 1
doAssert skipWhitespace("Hello World", 5) == 1 doAssert skipWhitespace("Hello World", 5) == 1
doAssert skipWhitespace("Hello World", 5) == 2 doAssert skipWhitespace("Hello World", 5) == 2
result = 0
while start+result < s.len and s[start+result] in Whitespace: inc(result) while start+result < s.len and s[start+result] in Whitespace: inc(result)
proc skip*(s, token: string, start = 0): int {.inline.} = proc skip*(s, token: string, start = 0): int {.inline.} =
@ -260,6 +261,7 @@ proc skip*(s, token: string, start = 0): int {.inline.} =
doAssert skip("2019-01-22", "19", 2) == 2 doAssert skip("2019-01-22", "19", 2) == 2
doAssert skip("CAPlow", "CAP", 0) == 3 doAssert skip("CAPlow", "CAP", 0) == 3
doAssert skip("CAPlow", "cap", 0) == 0 doAssert skip("CAPlow", "cap", 0) == 0
result = 0
while start+result < s.len and result < token.len and while start+result < s.len and result < token.len and
s[result+start] == token[result]: s[result+start] == token[result]:
inc(result) inc(result)
@ -270,6 +272,7 @@ proc skipIgnoreCase*(s, token: string, start = 0): int =
runnableExamples: runnableExamples:
doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3 doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3 doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
result = 0
while start+result < s.len and result < token.len and while start+result < s.len and result < token.len and
toLower(s[result+start]) == toLower(token[result]): inc(result) toLower(s[result+start]) == toLower(token[result]): inc(result)
if result != token.len: result = 0 if result != token.len: result = 0
@ -282,6 +285,7 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1 doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
doAssert skipUntil("Hello World", {'W'}, 0) == 6 doAssert skipUntil("Hello World", {'W'}, 0) == 6
doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6 doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
result = 0
while start+result < s.len and s[result+start] notin until: inc(result) while start+result < s.len and s[result+start] notin until: inc(result)
proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
@ -293,6 +297,7 @@ proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
doAssert skipUntil("Hello World", 'o', 4) == 0 doAssert skipUntil("Hello World", 'o', 4) == 0
doAssert skipUntil("Hello World", 'W', 0) == 6 doAssert skipUntil("Hello World", 'W', 0) == 6
doAssert skipUntil("Hello World", 'w', 0) == 11 doAssert skipUntil("Hello World", 'w', 0) == 11
result = 0
while start+result < s.len and s[result+start] != until: inc(result) while start+result < s.len and s[result+start] != until: inc(result)
proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
@ -302,6 +307,7 @@ proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
doAssert skipWhile("Hello World", {'H', 'e'}) == 2 doAssert skipWhile("Hello World", {'H', 'e'}) == 2
doAssert skipWhile("Hello World", {'e'}) == 0 doAssert skipWhile("Hello World", {'e'}) == 0
doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3 doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
result = 0
while start+result < s.len and s[result+start] in toSkip: inc(result) while start+result < s.len and s[result+start] in toSkip: inc(result)
proc parseUntil*(s: string, token: var string, until: set[char], proc parseUntil*(s: string, token: var string, until: set[char],
@ -437,7 +443,7 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
var res: BiggestInt var res: BiggestInt
doAssert parseBiggestInt("9223372036854775807", res, 0) == 19 doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
doAssert res == 9223372036854775807 doAssert res == 9223372036854775807
var res: BiggestInt var res = BiggestInt(0)
# use 'res' for exception safety (don't write to 'number' in case of an # use 'res' for exception safety (don't write to 'number' in case of an
# overflow exception): # overflow exception):
result = rawParseInt(s, res, start) result = rawParseInt(s, res, start)
@ -455,7 +461,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
doAssert res == 2019 doAssert res == 2019
doAssert parseInt("2019", res, 2) == 2 doAssert parseInt("2019", res, 2) == 2
doAssert res == 19 doAssert res == 19
var res: BiggestInt var res = BiggestInt(0)
result = parseBiggestInt(s, res, start) result = parseBiggestInt(s, res, start)
when sizeof(int) <= 4: when sizeof(int) <= 4:
if res < low(int) or res > high(int): if res < low(int) or res > high(int):
@ -518,7 +524,7 @@ proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
doAssert res == 12 doAssert res == 12
doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19 doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
doAssert res == 1111111111111111111'u64 doAssert res == 1111111111111111111'u64
var res: BiggestUInt var res = BiggestUInt(0)
# use 'res' for exception safety (don't write to 'number' in case of an # use 'res' for exception safety (don't write to 'number' in case of an
# overflow exception): # overflow exception):
result = rawParseUInt(s, res, start) result = rawParseUInt(s, res, start)
@ -536,7 +542,7 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {.
doAssert res == 3450 doAssert res == 3450
doAssert parseUInt("3450", res, 2) == 2 doAssert parseUInt("3450", res, 2) == 2
doAssert res == 50 doAssert res == 50
var res: BiggestUInt var res = BiggestUInt(0)
result = parseBiggestUInt(s, res, start) result = parseBiggestUInt(s, res, start)
when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4: when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
if res > 0xFFFF_FFFF'u64: if res > 0xFFFF_FFFF'u64:
@ -563,7 +569,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
doAssert res == 32.57 doAssert res == 32.57
doAssert parseFloat("32.57", res, 3) == 2 doAssert parseFloat("32.57", res, 3) == 2
doAssert res == 57.00 doAssert res == 57.00
var bf: BiggestFloat var bf = BiggestFloat(0.0)
result = parseBiggestFloat(s, bf, start) result = parseBiggestFloat(s, bf, start)
if result != 0: if result != 0:
number = bf number = bf

View file

@ -44,7 +44,7 @@ proc next*(it: var PathIter; x: string): (int, int) =
it.notFirst = true it.notFirst = true
iterator dirs(x: string): (int, int) = iterator dirs(x: string): (int, int) =
var it: PathIter var it = default PathIter
while hasNext(it, x): yield next(it, x) while hasNext(it, x): yield next(it, x)
proc isDot(x: string; bounds: (int, int)): bool = proc isDot(x: string; bounds: (int, int)): bool =

View file

@ -1103,6 +1103,7 @@ proc parseInt*(s: string): int {.noSideEffect,
## If `s` is not a valid integer, `ValueError` is raised. ## If `s` is not a valid integer, `ValueError` is raised.
runnableExamples: runnableExamples:
doAssert parseInt("-0042") == -42 doAssert parseInt("-0042") == -42
result = 0
let L = parseutils.parseInt(s, result, 0) let L = parseutils.parseInt(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s) raise newException(ValueError, "invalid integer: " & s)
@ -1112,6 +1113,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect,
## Parses a decimal integer value contained in `s`. ## Parses a decimal integer value contained in `s`.
## ##
## If `s` is not a valid integer, `ValueError` is raised. ## If `s` is not a valid integer, `ValueError` is raised.
result = BiggestInt(0)
let L = parseutils.parseBiggestInt(s, result, 0) let L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s) raise newException(ValueError, "invalid integer: " & s)
@ -1121,6 +1123,7 @@ proc parseUInt*(s: string): uint {.noSideEffect,
## Parses a decimal unsigned integer value contained in `s`. ## Parses a decimal unsigned integer value contained in `s`.
## ##
## If `s` is not a valid integer, `ValueError` is raised. ## If `s` is not a valid integer, `ValueError` is raised.
result = uint(0)
let L = parseutils.parseUInt(s, result, 0) let L = parseutils.parseUInt(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid unsigned integer: " & s) raise newException(ValueError, "invalid unsigned integer: " & s)
@ -1130,6 +1133,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect,
## Parses a decimal unsigned integer value contained in `s`. ## Parses a decimal unsigned integer value contained in `s`.
## ##
## If `s` is not a valid integer, `ValueError` is raised. ## If `s` is not a valid integer, `ValueError` is raised.
result = BiggestUInt(0)
let L = parseutils.parseBiggestUInt(s, result, 0) let L = parseutils.parseBiggestUInt(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid unsigned integer: " & s) raise newException(ValueError, "invalid unsigned integer: " & s)
@ -1143,6 +1147,7 @@ proc parseFloat*(s: string): float {.noSideEffect,
runnableExamples: runnableExamples:
doAssert parseFloat("3.14") == 3.14 doAssert parseFloat("3.14") == 3.14
doAssert parseFloat("inf") == 1.0/0 doAssert parseFloat("inf") == 1.0/0
result = 0.0
let L = parseutils.parseFloat(s, result, 0) let L = parseutils.parseFloat(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid float: " & s) raise newException(ValueError, "invalid float: " & s)
@ -1161,6 +1166,7 @@ proc parseBinInt*(s: string): int {.noSideEffect,
doAssert a.parseBinInt() == 53 doAssert a.parseBinInt() == 53
doAssert b.parseBinInt() == 7 doAssert b.parseBinInt() == 7
result = 0
let L = parseutils.parseBin(s, result, 0) let L = parseutils.parseBin(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid binary integer: " & s) raise newException(ValueError, "invalid binary integer: " & s)
@ -1172,6 +1178,7 @@ proc parseOctInt*(s: string): int {.noSideEffect,
## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one
## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## of the following optional prefixes: ``0o``, ``0O``. Underscores within
## `s` are ignored. ## `s` are ignored.
result = 0
let L = parseutils.parseOct(s, result, 0) let L = parseutils.parseOct(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid oct integer: " & s) raise newException(ValueError, "invalid oct integer: " & s)
@ -1183,6 +1190,7 @@ proc parseHexInt*(s: string): int {.noSideEffect,
## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
## within `s` are ignored. ## within `s` are ignored.
result = 0
let L = parseutils.parseHex(s, result, 0) let L = parseutils.parseHex(s, result, 0)
if L != s.len or L == 0: if L != s.len or L == 0:
raise newException(ValueError, "invalid hex integer: " & s) raise newException(ValueError, "invalid hex integer: " & s)
@ -1270,9 +1278,9 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped =
result = nnkCaseStmt.newTree(nnkDotExpr.newTree(argSym, result = nnkCaseStmt.newTree(nnkDotExpr.newTree(argSym,
bindSym"nimIdentNormalize")) bindSym"nimIdentNormalize"))
# stores all processed field strings to give error msg for ambiguous enums # stores all processed field strings to give error msg for ambiguous enums
var foundFields: seq[string] var foundFields: seq[string] = @[]
var fStr: string # string of current field var fStr = "" # string of current field
var fNum: BiggestInt # int value of current field var fNum = BiggestInt(0) # int value of current field
for f in impl: for f in impl:
case f.kind case f.kind
of nnkEmpty: continue # skip first node of `enumTy` of nnkEmpty: continue # skip first node of `enumTy`
@ -2028,6 +2036,7 @@ proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect,
if sub.len == 0: if sub.len == 0:
return -1 return -1
let last = if last == -1: s.high else: last let last = if last == -1: s.high else: last
result = 0
for i in countdown(last - sub.len + 1, start): for i in countdown(last - sub.len + 1, start):
for j in 0..sub.len-1: for j in 0..sub.len-1:
result = i result = i
@ -2044,6 +2053,7 @@ proc count*(s: string, sub: char): int {.noSideEffect,
## ##
## See also: ## See also:
## * `countLines proc<#countLines,string>`_ ## * `countLines proc<#countLines,string>`_
result = 0
for c in s: for c in s:
if c == sub: inc result if c == sub: inc result
@ -2054,6 +2064,7 @@ proc count*(s: string, subs: set[char]): int {.noSideEffect,
## See also: ## See also:
## * `countLines proc<#countLines,string>`_ ## * `countLines proc<#countLines,string>`_
doAssert card(subs) > 0 doAssert card(subs) > 0
result = 0
for c in s: for c in s:
if c in subs: inc result if c in subs: inc result
@ -2066,6 +2077,7 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
## See also: ## See also:
## * `countLines proc<#countLines,string>`_ ## * `countLines proc<#countLines,string>`_
doAssert sub.len > 0 doAssert sub.len > 0
result = 0
var i = 0 var i = 0
while true: while true:
i = s.find(sub, i) i = s.find(sub, i)
@ -2313,7 +2325,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
case s[i+1]: case s[i+1]:
of 'x': of 'x':
inc i, 2 inc i, 2
var c: int var c = 0
i += parseutils.parseHex(s, c, i, maxLen = 2) i += parseutils.parseHex(s, c, i, maxLen = 2)
result.add(chr(c)) result.add(chr(c))
dec i, 2 dec i, 2
@ -2514,7 +2526,7 @@ proc formatSize*(bytes: int64,
xb: int64 = bytes xb: int64 = bytes
fbytes: float fbytes: float
lastXb: int64 = bytes lastXb: int64 = bytes
matchedIndex: int matchedIndex = 0
prefixes: array[9, string] prefixes: array[9, string]
if prefix == bpColloquial: if prefix == bpColloquial:
prefixes = collPrefixes prefixes = collPrefixes

View file

@ -895,16 +895,16 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} =
millis mod convert(Seconds, Milliseconds, 1).int) millis mod convert(Seconds, Milliseconds, 1).int)
result = initTime(seconds, nanos) result = initTime(seconds, nanos)
elif defined(macosx): elif defined(macosx):
var a: Timeval var a {.noinit.}: Timeval
gettimeofday(a) gettimeofday(a)
result = initTime(a.tv_sec.int64, result = initTime(a.tv_sec.int64,
convert(Microseconds, Nanoseconds, a.tv_usec.int)) convert(Microseconds, Nanoseconds, a.tv_usec.int))
elif defined(posix): elif defined(posix):
var ts: Timespec var ts {.noinit.}: Timespec
discard clock_gettime(CLOCK_REALTIME, ts) discard clock_gettime(CLOCK_REALTIME, ts)
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
elif defined(windows): elif defined(windows):
var f: FILETIME var f {.noinit.}: FILETIME
getSystemTimeAsFileTime(f) getSystemTimeAsFileTime(f)
result = fromWinTime(rdFileTime(f)) result = fromWinTime(rdFileTime(f))
@ -1756,7 +1756,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string,
proc parsePattern(input: string, pattern: FormatPattern, i: var int, proc parsePattern(input: string, pattern: FormatPattern, i: var int,
parsed: var ParsedTime, loc: DateTimeLocale): bool = parsed: var ParsedTime, loc: DateTimeLocale): bool =
template takeInt(allowedWidth: Slice[int], allowSign = false): int = template takeInt(allowedWidth: Slice[int], allowSign = false): int =
var sv: int var sv = 0
var pd = parseInt(input, sv, i, allowedWidth.b, allowSign) var pd = parseInt(input, sv, i, allowedWidth.b, allowSign)
if pd < allowedWidth.a: if pd < allowedWidth.a:
return false return false
@ -1987,6 +1987,7 @@ proc format*(dt: DateTime, f: TimeFormat,
let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc())
doAssert "2000-01-01" == dt.format(f) doAssert "2000-01-01" == dt.format(f)
assertDateTimeInitialized dt assertDateTimeInitialized dt
result = ""
var idx = 0 var idx = 0
while idx <= f.patterns.high: while idx <= f.patterns.high:
case f.patterns[idx].FormatPattern case f.patterns[idx].FormatPattern
@ -2284,7 +2285,7 @@ proc between*(startDt, endDt: DateTime): TimeInterval =
endDate.monthday.dec endDate.monthday.dec
# Years # Years
result.years.inc endDate.year - startDate.year - 1 result.years = endDate.year - startDate.year - 1
if (startDate.month, startDate.monthday) <= (endDate.month, endDate.monthday): if (startDate.month, startDate.monthday) <= (endDate.month, endDate.monthday):
result.years.inc result.years.inc
startDate.year.inc result.years startDate.year.inc result.years
@ -2441,6 +2442,7 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval):
var months = interval.years * 12 + interval.months var months = interval.years * 12 + interval.months
var curYear = dt.year var curYear = dt.year
var curMonth = dt.month var curMonth = dt.month
result = default(tuple[adjDur, absDur: Duration])
# Subtracting # Subtracting
if months < 0: if months < 0:
for mth in countdown(-1 * months, 1): for mth in countdown(-1 * months, 1):
@ -2562,17 +2564,17 @@ proc epochTime*(): float {.tags: [TimeEffect].} =
## ##
## ``getTime`` should generally be preferred over this proc. ## ``getTime`` should generally be preferred over this proc.
when defined(macosx): when defined(macosx):
var a: Timeval var a {.noinit.}: Timeval
gettimeofday(a) gettimeofday(a)
result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat( result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat(
a.tv_usec)*0.00_0001 a.tv_usec)*0.00_0001
elif defined(posix): elif defined(posix):
var ts: Timespec var ts {.noinit.}: Timespec
discard clock_gettime(CLOCK_REALTIME, ts) discard clock_gettime(CLOCK_REALTIME, ts)
result = toBiggestFloat(ts.tv_sec.int64) + result = toBiggestFloat(ts.tv_sec.int64) +
toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000 toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000
elif defined(windows): elif defined(windows):
var f: winlean.FILETIME var f {.noinit.}: winlean.FILETIME
getSystemTimeAsFileTime(f) getSystemTimeAsFileTime(f)
var i64 = rdFileTime(f) - epochDiff var i64 = rdFileTime(f) - epochDiff
var secs = i64 div rateDiff var secs = i64 div rateDiff

View file

@ -112,7 +112,7 @@ macro genericParamsImpl(T: typedesc): untyped =
of nnkBracketExpr: of nnkBracketExpr:
for i in 1..<impl.len: for i in 1..<impl.len:
let ai = impl[i] let ai = impl[i]
var ret: NimNode var ret: NimNode = nil
case ai.typeKind case ai.typeKind
of ntyTypeDesc: of ntyTypeDesc:
ret = ai ret = ai

View file

@ -39,6 +39,7 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
doAssert a.runeLen == 6 doAssert a.runeLen == 6
## note: a.len == 8 ## note: a.len == 8
result = 0
var i = 0 var i = 0
while i < len(s): while i < len(s):
if uint(s[i]) <= 127: inc(i) if uint(s[i]) <= 127: inc(i)

View file

@ -4,7 +4,7 @@ const NimStackTraceMsgs = compileOption("stacktraceMsgs")
template procName*(): string = template procName*(): string =
## returns current C/C++ function name ## returns current C/C++ function name
when defined(c) or defined(cpp): when defined(c) or defined(cpp):
var name {.inject.}: cstring var name {.inject, noinit.}: cstring
{.emit: "`name` = __func__;".} {.emit: "`name` = __func__;".}
$name $name
@ -12,7 +12,7 @@ template getPFrame*(): PFrame =
## avoids a function call (unlike `getFrame()`) ## avoids a function call (unlike `getFrame()`)
block: block:
when NimStackTrace: when NimStackTrace:
var framePtr {.inject.}: PFrame var framePtr {.inject, noinit.}: PFrame
{.emit: "`framePtr` = &FR_;".} {.emit: "`framePtr` = &FR_;".}
framePtr framePtr
@ -21,7 +21,7 @@ template setFrameMsg*(msg: string, prefix = " ") =
## in a given PFrame. Noop unless passing --stacktraceMsgs and --stacktrace ## in a given PFrame. Noop unless passing --stacktraceMsgs and --stacktrace
when NimStackTrace and NimStackTraceMsgs: when NimStackTrace and NimStackTraceMsgs:
block: block:
var fr {.inject.}: PFrame var fr {.inject, noinit.}: PFrame
{.emit: "`fr` = &FR_;".} {.emit: "`fr` = &FR_;".}
# consider setting a custom upper limit on size (analog to stack overflow) # consider setting a custom upper limit on size (analog to stack overflow)
frameMsgBuf.setLen fr.frameMsgLen frameMsgBuf.setLen fr.frameMsgLen

View file

@ -1767,6 +1767,7 @@ export iterators
proc find*[T, S](a: T, item: S): int {.inline.}= proc find*[T, S](a: T, item: S): int {.inline.}=
## Returns the first index of `item` in `a` or -1 if not found. This requires ## Returns the first index of `item` in `a` or -1 if not found. This requires
## appropriate `items` and `==` operations to work. ## appropriate `items` and `==` operations to work.
result = 0
for i in items(a): for i in items(a):
if i == item: return if i == item: return
inc(result) inc(result)
@ -2105,7 +2106,7 @@ when not defined(js):
# Linux 64bit system. -- That's because the stack direction is the other # Linux 64bit system. -- That's because the stack direction is the other
# way around. # way around.
when declared(nimGC_setStackBottom): when declared(nimGC_setStackBottom):
var locals {.volatile.}: pointer var locals {.volatile, noinit.}: pointer
locals = addr(locals) locals = addr(locals)
nimGC_setStackBottom(locals) nimGC_setStackBottom(locals)
@ -2274,7 +2275,9 @@ when notJSnotNims:
of 2: d = uint(cast[ptr uint16](a + uint(n.offset))[]) of 2: d = uint(cast[ptr uint16](a + uint(n.offset))[])
of 4: d = uint(cast[ptr uint32](a + uint(n.offset))[]) of 4: d = uint(cast[ptr uint32](a + uint(n.offset))[])
of 8: d = uint(cast[ptr uint64](a + uint(n.offset))[]) of 8: d = uint(cast[ptr uint64](a + uint(n.offset))[])
else: sysAssert(false, "getDiscriminant: invalid n.typ.size") else:
d = 0'u
sysAssert(false, "getDiscriminant: invalid n.typ.size")
return d return d
proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode = proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode =

View file

@ -586,7 +586,8 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
proc getBigChunk(a: var MemRegion, size: int): PBigChunk = proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
sysAssert(size > 0, "getBigChunk 2") sysAssert(size > 0, "getBigChunk 2")
var size = size # roundup(size, PageSize) var size = size # roundup(size, PageSize)
var fl, sl: int var fl = 0
var sl = 0
mappingSearch(size, fl, sl) mappingSearch(size, fl, sl)
sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk") sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
result = findSuitableBlock(a, fl, sl) result = findSuitableBlock(a, fl, sl)

View file

@ -11,6 +11,7 @@ proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.}
proc `$`(info: InstantiationInfo): string = proc `$`(info: InstantiationInfo): string =
# The +1 is needed here # The +1 is needed here
# instead of overriding `$` (and changing its meaning), consider explicit name. # instead of overriding `$` (and changing its meaning), consider explicit name.
result = ""
result.toLocation(info.filename, info.line, info.column+1) result.toLocation(info.filename, info.line, info.column+1)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View file

@ -217,7 +217,7 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} =
when nimCoroutines: when nimCoroutines:
var pos = stack.pos var pos = stack.pos
else: else:
var pos {.volatile.}: pointer var pos {.volatile, noinit.}: pointer
pos = addr(pos) pos = addr(pos)
if pos != nil: if pos != nil:
@ -229,6 +229,7 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} =
result = 0 result = 0
proc stackSize(): int {.noinline.} = proc stackSize(): int {.noinline.} =
result = 0
for stack in gch.stack.items(): for stack in gch.stack.items():
result = result + stack.stackSize() result = result + stack.stackSize()
@ -303,7 +304,7 @@ when not defined(useNimRtl):
{.pop.} {.pop.}
proc isOnStack(p: pointer): bool = proc isOnStack(p: pointer): bool =
var stackTop {.volatile.}: pointer var stackTop {.volatile, noinit.}: pointer
stackTop = addr(stackTop) stackTop = addr(stackTop)
var a = cast[ByteAddress](gch.getActiveStack().bottom) var a = cast[ByteAddress](gch.getActiveStack().bottom)
var b = cast[ByteAddress](stackTop) var b = cast[ByteAddress](stackTop)

View file

@ -424,12 +424,12 @@ proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
else: write(f, "false") else: write(f, "false")
proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} = proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
var buffer: array[65, char] var buffer {.noinit.}: array[65, char]
discard writeFloatToBuffer(buffer, r) discard writeFloatToBuffer(buffer, r)
if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f) 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], benign.} =
var buffer: array[65, char] var buffer {.noinit.}: array[65, char]
discard writeFloatToBuffer(buffer, r) discard writeFloatToBuffer(buffer, r)
if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f) if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
@ -591,7 +591,7 @@ when defined(posix) and not defined(nimscript):
proc open*(f: var File, filename: string, proc open*(f: var File, filename: string,
mode: FileMode = fmRead, mode: FileMode = fmRead,
bufSize: int = -1): bool {.tags: [], raises: [], benign.} = bufSize: int = -1): bool {.tags: [], raises: [], benign.} =
## Opens a file named `filename` with given `mode`. ## Opens a file named `filename` with given `mode`.
## ##
## Default mode is readonly. Returns true if the file could be opened. ## Default mode is readonly. Returns true if the file could be opened.
@ -605,7 +605,7 @@ proc open*(f: var File, filename: string,
# How `fopen` handles opening a directory is not specified in ISO C and # How `fopen` handles opening a directory is not specified in ISO C and
# POSIX. We do not want to handle directories as regular files that can # POSIX. We do not want to handle directories as regular files that can
# be opened. # be opened.
var res: Stat var res {.noinit.}: Stat
if c_fstat(getFileHandle(f2), res) >= 0'i32 and modeIsDir(res.st_mode): if c_fstat(getFileHandle(f2), res) >= 0'i32 and modeIsDir(res.st_mode):
close(f2) close(f2)
return false return false
@ -759,7 +759,7 @@ proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
## Raises an IO exception in case of an error. If you need to call ## Raises an IO exception in case of an error. If you need to call
## this inside a compile time macro you can use `staticRead ## this inside a compile time macro you can use `staticRead
## <system.html#staticRead,string>`_. ## <system.html#staticRead,string>`_.
var f: File var f: File = nil
if open(f, filename): if open(f, filename):
try: try:
result = readAll(f) result = readAll(f)
@ -772,7 +772,7 @@ proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} =
## Opens a file named `filename` for writing. Then writes the ## Opens a file named `filename` for writing. Then writes the
## `content` completely to the file and closes the file afterwards. ## `content` completely to the file and closes the file afterwards.
## Raises an IO exception in case of an error. ## Raises an IO exception in case of an error.
var f: File var f: File = nil
if open(f, filename, fmWrite): if open(f, filename, fmWrite):
try: try:
f.write(content) f.write(content)
@ -785,7 +785,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} =
## Opens a file named `filename` for writing. Then writes the ## Opens a file named `filename` for writing. Then writes the
## `content` completely to the file and closes the file afterwards. ## `content` completely to the file and closes the file afterwards.
## Raises an IO exception in case of an error. ## Raises an IO exception in case of an error.
var f: File var f: File = nil
if open(f, filename, fmWrite): if open(f, filename, fmWrite):
try: try:
f.writeBuffer(unsafeAddr content[0], content.len) f.writeBuffer(unsafeAddr content[0], content.len)
@ -799,7 +799,7 @@ proc readLines*(filename: string, n: Natural): seq[TaintedString] =
## in case of an error. Raises EOF if file does not contain at least `n` lines. ## in case of an error. Raises EOF if file does not contain at least `n` lines.
## Available at compile time. A line of text may be delimited by ``LF`` or ``CRLF``. ## Available at compile time. A line of text may be delimited by ``LF`` or ``CRLF``.
## The newline character(s) are not part of the returned strings. ## The newline character(s) are not part of the returned strings.
var f: File var f: File = nil
if open(f, filename): if open(f, filename):
try: try:
result = newSeq[TaintedString](n) result = newSeq[TaintedString](n)

View file

@ -102,6 +102,7 @@ proc reprSetAux(result: var string, p: pointer, typ: PNimType) =
of 4: u = cast[ptr uint32](p)[] of 4: u = cast[ptr uint32](p)[]
of 8: u = cast[ptr uint64](p)[] of 8: u = cast[ptr uint64](p)[]
else: else:
u = uint64(0)
var a = cast[PByteArray](p) var a = cast[PByteArray](p)
for i in 0 .. typ.size*8-1: for i in 0 .. typ.size*8-1:
if (uint(a[i shr 3]) and (1'u shl (i and 7))) != 0: if (uint(a[i shr 3]) and (1'u shl (i and 7))) != 0:

View file

@ -31,6 +31,7 @@ proc countBits64(n: uint64): int {.compilerproc, inline.} =
proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} =
var i = 0 var i = 0
result = 0
when defined(x86) or defined(amd64): when defined(x86) or defined(amd64):
while i < len - 8: while i < len - 8:
inc(result, countBits64((cast[ptr uint64](s[i].unsafeAddr))[])) inc(result, countBits64((cast[ptr uint64](s[i].unsafeAddr))[]))

View file

@ -94,7 +94,7 @@ proc addFloat*(result: var string; x: float) =
when nimvm: when nimvm:
result.add $x result.add $x
else: else:
var buffer: array[65, char] var buffer {.noinit.}: array[65, char]
let n = writeFloatToBuffer(buffer, x) let n = writeFloatToBuffer(buffer, x)
result.addCstringN(cstring(buffer[0].addr), n) result.addCstringN(cstring(buffer[0].addr), n)
@ -131,8 +131,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
i = start i = start
sign = 1.0 sign = 1.0
kdigits, fdigits = 0 kdigits, fdigits = 0
exponent: int exponent = 0
integer: uint64 integer = uint64(0)
fracExponent = 0 fracExponent = 0
expSign = 1 expSign = 1
firstDigit = -1 firstDigit = -1

View file

@ -63,6 +63,7 @@ proc ord(arg: Utf16Char): int = int(cast[uint16](arg))
proc len*(w: WideCString): int = proc len*(w: WideCString): int =
## returns the length of a widestring. This traverses the whole string to ## returns the length of a widestring. This traverses the whole string to
## find the binary zero end marker! ## find the binary zero end marker!
result = 0
while int16(w[result]) != 0'i16: inc result while int16(w[result]) != 0'i16: inc result
const const

View file

@ -6,8 +6,8 @@ discard """
const expected = """ const expected = """
tassert_c.nim(35) tassert_c tassert_c.nim(35) tassert_c
tassert_c.nim(34) foo tassert_c.nim(34) foo
assertions.nim(29) failedAssertImpl assertions.nim(30) failedAssertImpl
assertions.nim(22) raiseAssert assertions.nim(23) raiseAssert
fatal.nim(49) sysFatal""" fatal.nim(49) sysFatal"""
proc tmatch(x, p: string): bool = proc tmatch(x, p: string): bool =
@ -36,4 +36,5 @@ try:
except AssertionDefect: except AssertionDefect:
let e = getCurrentException() let e = getCurrentException()
let trace = e.getStackTrace let trace = e.getStackTrace
if tmatch(trace, expected): echo true else: echo "wrong trace:\n" & trace if tmatch(trace, expected): echo true
else: echo "wrong trace:\n" & trace

View file

@ -0,0 +1,28 @@
discard """
cmd: '''nim c --warningAsError[Uninit]:on --skipCfg --skipParentCfg $file'''
errormsg: "use explicit initialization of 'x' for clarity [Uninit]"
line: 24
disabled: "true"
"""
proc gah[T](x: out T) =
x = 3
proc main =
var a: array[2, int]
var x: int
gah(x)
a[0] = 3
a[x] = 3
echo x
main()
proc mainB =
var a: array[2, int]
var x: int
a[0] = 3
a[x] = 3
echo x
mainB()

View file

@ -1,5 +1,5 @@
discard """ discard """
nimout: "Warning: 'y' might not have been initialized [Uninit]" nimout: "Warning: use explicit initialization of 'y' for clarity [Uninit]"
line:34 line:34
action: compile action: compile
""" """