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
parent a9eee6db65
commit da29222f86
53 changed files with 355 additions and 257 deletions

View File

@@ -60,7 +60,7 @@
accept an existing string to modify, which avoids memory
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`
@@ -116,7 +116,11 @@
- Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
## 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
type
@@ -175,6 +179,8 @@ proc mydiv(a, b): int {.raises: [].} =
The reason for this is that `DivByZeroDefect` inherits from `Defect` and
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
- Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop.

View File

@@ -435,7 +435,8 @@ type
# be any type.
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
# now different from tyEmpty, hurray!
@@ -1802,12 +1803,12 @@ proc skipStmtList*(n: PNode): PNode =
else:
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
## returned. Otherwise ``typ`` is simply returned as-is.
result = typ
if typ.kind != tyVar:
result = newType(tyVar, typ.owner)
if typ.kind != kind:
result = newType(kind, typ.owner)
rawAddSon(result, typ)
proc toRef*(typ: PType): PType =
@@ -1934,3 +1935,6 @@ proc toHumanStr*(kind: TSymKind): string =
proc toHumanStr*(kind: TTypeKind): string =
## strips leading `tk`
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]
of tyString, tySequence:
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:
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]
else:
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)]
of tyString, tySequence:
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:
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
t.r = "(*$1)" % [a.rdLoc]
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]):
initLocExpr(p, n, 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:
initLocExprSingleUse(p, n[0], a)
# 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)
if paramType.typ.isCompileTimeOnly:
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])
else:
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]
while ri.kind == nkObjDownConv: ri = ri[0]
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
if x.typ.kind == tyPtr:
result = genArgNoParam(p, x)

View File

@@ -126,9 +126,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope =
result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size)))
proc genSetNode(p: BProc, n: PNode): Rope =
var cs: TBitSet
var size = int(getSize(p.config, n.typ))
toBitSet(p.config, n, cs)
let cs = toBitSet(p.config, n)
if size > 8:
let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
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.} =
result = p.module.compileToCpp and
skipTypes(typ, abstractInstOwned).kind == tyVar and
skipTypes(typ, abstractInstOwned).kind in {tyVar} and
tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
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:
typ = typ.lastSon
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)
return
else:
@@ -716,7 +715,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
else:
internalError(p.config, e.info, "genDeref " & $typ.kind)
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:
putIntoDest(p, d, e, rdLoc(a), a.storage)
return
@@ -2960,8 +2959,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope =
ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind
case ty
of tySet:
var cs: TBitSet
toBitSet(p.config, n, cs)
let cs = toBitSet(p.config, n)
result = genRawSetData(cs, int(getSize(p.config, n.typ)))
of tySequence:
if optSeqDestructors in p.config.globalOptions:

View File

@@ -701,7 +701,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
return
case t.kind
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: "*"
var et = origTyp.skipTypes(abstractInst).lastSon
var etB = et.skipTypes(abstractInst)

View File

@@ -715,10 +715,8 @@ proc genCall(c: var Con; n: PNode) =
for i in 1..<n.len:
gen(c, n[i])
when false:
if t != nil and i < t.len and t[i].kind == tyVar:
# This is wrong! Pass by var is a 'might def', not a 'must def'
# like the other defs we emit. This is not good enough for a move
# optimizer.
if t != nil and i < t.len and t[i].kind == tyOut:
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
genDef(c, n[i])
# every call can potentially raise:
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}:
result = true
elif n.sym.kind == skParam and skipTypes(n.sym.typ,
abstractInst).kind != tyVar:
abstractInst).kind notin {tyVar}:
result = true
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",
[a.rdLoc, b.res, genTypeInfo(p, y.typ)])
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])
else:
useMagic(p, "nimCopy")

View File

@@ -113,7 +113,7 @@ const
warnStaticIndexCheck: "$1",
warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized",
warnUninit: "use explicit initialization of '$1' for clarity",
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.",
warnLockLevel: "$1",

View File

@@ -59,7 +59,7 @@ proc newFastAsgnStmt*(le, ri: PNode): PNode =
result[0] = le
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[0] = le
result[1] = newNodeIT(nkCall, ri.info, ri.typ)

View File

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

View File

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

View File

@@ -214,7 +214,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arNone
case n.kind
of nkEmpty:
if n.typ != nil and n.typ.kind == tyVar:
if n.typ != nil and n.typ.kind in {tyVar}:
result = arLValue
of nkSym:
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
elif n.sym.kind == skType:
let t = n.sym.typ.skipTypes({tyTypeDesc})
if t.kind == tyVar: result = arStrange
if t.kind in {tyVar}: result = arStrange
of nkDotExpr:
let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc})
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:
if getMagic(n) in {mArrGet, mSlice}:
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
elif isUnsafeAddr and n.typ != nil and n.typ.kind == tyLent:
result = arLValue

View File

@@ -793,7 +793,7 @@ proc primarySuffix(p: var TParser, r: PNode,
break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast,
tkOpr, tkDotDot, tkVar, tkStatic, tkType, tkEnum, tkTuple,
tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple,
tkObject, tkProc:
# XXX: In type sections we allow the free application of the
# command syntax, with the exception of expressions such as
@@ -1300,6 +1300,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
optInd(p, result)
result.add(primary(p, pmNormal))
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 tkPtr: result = parseTypeDescKAux(p, nkPtrTy, 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 skip(t: PType): PType =
result = t
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
tyOrdinal, tyAlias, tySink}:
while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
result = lastSon(result)
let typ = n.typ.skip
@@ -883,7 +883,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
case n.kind
of nkClosedSymChoice, nkOpenSymChoice:
if n.len > 0: result = bracketKind(g, n[0])
of nkSym:
of nkSym:
result = case n.sym.name.s
of "[]": bkBracket
of "[]=": bkBracketAsgn
@@ -974,7 +974,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParRi, ")")
put(g, tkColon, ":")
gsub(g, n, n.len-1)
elif n.len >= 1:
elif n.len >= 1:
case bracketKind(g, n[0])
of bkBracket:
gsub(g, n, 1)

View File

@@ -107,6 +107,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string =
var indent = info.col
let numLines = numLines(conf, info.fileIndex).uint16
var lastNonemptyPos = 0
result = ""
var ldata = LineData(lineFirst: first.line.int, conf: conf)
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
var x: PType
if param.typ.kind == tyVar:
x = newTypeS(tyVar, c)
x = newTypeS(param.typ.kind, c)
x.addSonSkipIntLit t.baseOfDistinct
else:
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:
for i in 1..<n.len:
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]
if isAssignable(c, it) notin {arLValue, arLocalLValue}:
if it.kind != nkHiddenAddr:
@@ -733,7 +733,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
# calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n[i])
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:
n[i] = analyseIfAddressTaken(c, n[i])
@@ -1711,7 +1711,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
var le = a.typ
if le == nil:
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
skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}:
# Direct assignment to a discriminant is allowed!

View File

@@ -42,9 +42,6 @@ type
proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): 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 =
result = newNodeI(nkBracketExpr, n.info)
for i in 1..<n.len: result.add(n[i])

View File

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

View File

@@ -66,7 +66,7 @@ type
TEffects = object
exc: PNode # stack of exceptions
tags: PNode # list of tags
bottom, inTryStmt, inExceptOrFinallyStmt: int
bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn: int
owner: PSym
ownerModule: PSym
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 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.owner == a.owner and s.typ != nil
@@ -246,6 +247,18 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) =
var cycleCheck = initIntSet()
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) =
let s = n.sym
if a.inExceptOrFinallyStmt > 0:
@@ -257,20 +270,11 @@ proc useVar(a: PEffects, n: PNode) =
elif s.id notin a.init:
if s.typ.requiresInit:
message(a.config, n.info, warnProveInit, s.name.s)
else:
elif a.leftPartOfAsgn <= 0:
message(a.config, n.info, warnUninit, s.name.s)
# prevent superfluous warnings about the same variable:
a.init.add s.id
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)
useVarNoInitCheck(a, n, s)
type
@@ -538,7 +542,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} =
proc isTrival(caller: PNode): bool {.inline.} =
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 op = a.typ
# assume indirect calls are taken here:
@@ -572,7 +576,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode)
markGcUnsafe(tracked, a)
elif tfNoSideEffect notin op.flags:
markSideEffect(tracked, a)
if paramType != nil and paramType.kind == tyVar:
if paramType != nil and paramType.kind in {tyVar}:
invalidateFacts(tracked.guards, n)
if n.kind == nkSym and isLocalVar(tracked, n.sym):
makeVolatile(tracked, n.sym)
@@ -732,7 +736,7 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
optSeqDestructors in tracked.config.globalOptions:
tracked.owner.flags.incl sfInjectDestructors
proc track(tracked: PEffects, n: PNode) =
proc trackCall(tracked: PEffects; n: PNode) =
template gcsafeAndSideeffectCheck() =
if notGcSafe(op) and not importedFromC(a):
# 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):
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
of nkSym:
useVar(tracked, n)
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
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:
if n[0].kind != nkEmpty:
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.
addEffect(tracked, createRaise(tracked.graph, n), nil)
of nkCallKinds:
# 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: 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])
trackCall(tracked, n)
of nkDotExpr:
guardDotAccess(tracked, n)
for i in 0..<n.len: track(tracked, n[i])
@@ -856,7 +875,9 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, n[1])
initVar(tracked, n[0], volatileCheck=true)
invalidateFacts(tracked.guards, n[0])
inc tracked.leftPartOfAsgn
track(tracked, n[0])
dec tracked.leftPartOfAsgn
addAsgnFact(tracked.guards, n[0], n[1])
notNilCheck(tracked, n[1], n[0].typ)
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 n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple:
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:
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
(t.config.selectedGC in {gcArc, gcOrc} and isClosure(typ.skipTypes(abstractInst))):
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
(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])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
case iter.kind
of tyVar:
v.typ = newTypeS(tyVar, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
of tyLent:
v.typ = newTypeS(tyLent, c)
of tyVar, tyLent:
v.typ = newTypeS(iter.kind, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
@@ -794,13 +789,8 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
var v = symForVar(c, n[i])
if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal)
case iter.kind
of tyVar:
v.typ = newTypeS(tyVar, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
v.typ.flags.incl tfVarIsPtr
of tyLent:
v.typ = newTypeS(tyLent, c)
of tyVar, tyLent:
v.typ = newTypeS(iter.kind, c)
v.typ.add iterAfterVarLent[i]
if tfVarIsPtr in iter.flags:
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")
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:
result = newOrPrevType(tyVar, prev, c)
result = newOrPrevType(kind, prev, c)
var base = semTypeNode(c, n[0], nil).skipTypes({tyTypeDesc})
if base.kind == tyVar:
localError(c.config, n.info, "type 'var var' is not allowed")
base = base[0]
addSonSkipIntLit(result, base)
else:
result = newConstraint(c, tyVar)
result = newConstraint(c, kind)
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
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 nkRefTy: result = semAnyRef(c, n, tyRef, 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 nkStaticTy: result = semStaticType(c, n[0], prev)
of nkIteratorTy:

View File

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

View File

@@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
else: srFlowVar
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 containsGarbageCollectedRef(t): fvInvalid
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
# 'tyOpenArray':
var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len:
if i < formals.len:
if formals[i].typ.kind in {tyVar, tyLent}:
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
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:
result.typ = n.typ
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:
var m = n[0][1]
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:
result.typ = n.typ
elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
result.typ = toVar(result.typ)
result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind)
else:
if n[0].kind == a or n[0].kind == b:
# addr ( deref ( x )) --> x
@@ -569,14 +569,14 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
result = putArgInto(arg[0], formal)
of nkCurly, nkBracket:
for i in 0..<arg.len:
if putArgInto(arg[i], formal) != paDirectMapping:
if putArgInto(arg[i], formal) != paDirectMapping:
return paFastAsgn
result = paDirectMapping
of nkPar, nkTupleConstr, nkObjConstr:
for i in 0..<arg.len:
let a = if arg[i].kind == nkExprColonExpr: arg[i][1]
else: arg[0]
if putArgInto(a, formal) != paDirectMapping:
if putArgInto(a, formal) != paDirectMapping:
return paFastAsgn
result = paDirectMapping
else:
@@ -667,7 +667,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
idNodeTablePut(newC.mapping, formal, temp)
of paVarAsgn:
assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
assert(skipTypes(formal.typ, abstractInst).kind in {tyVar})
idNodeTablePut(newC.mapping, formal, arg)
# XXX BUG still not correct if the arg has a side effect!
of paComplexOpenarray:

View File

@@ -438,7 +438,7 @@ const
"lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor",
"and", "or", "not", "any", "static", "TypeFromExpr", "out ",
"void"]
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)
let paramType = typ.n[i]
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)
else:
allRoots(it, result, info)
@@ -164,7 +164,7 @@ proc depsArgs(w: var W; n: PNode) =
let paramType = typ.n[i]
if paramType.typ.isCompileTimeOnly: continue
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))
destInfo.incl markAsWrittenTo
if sfEscapes in paramType.sym.flags:
@@ -247,7 +247,7 @@ proc markWriteOrEscape(w: var W; conf: ConfigRef) =
for p in a.dest:
if p.kind == skParam and p.owner == w.owner:
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)
if {rootIsResultOrParam, rootIsHeapAccess, markAsEscaping}*a.destInfo != {}:

View File

@@ -2458,12 +2458,13 @@ matches) is preferred:
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
type checking, the argument is checked to be an `l-value`:idx:. ``var T``
matches better than just ``T`` then.
If the formal parameter ``f`` is of type ``var T`` (or ``out T``)
in addition to the ordinary
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
proc sayHi(x: int): string =
@@ -2482,6 +2483,17 @@ matches better than just ``T`` then.
# 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
--------------------------------
@@ -4230,9 +4242,7 @@ error message from ``e``, and for such situations it is enough to use
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.
A custom exception is a custom type:
Is it possible to create custom exceptions. A custom exception is a custom type:
.. code-block:: nim
type
@@ -4782,7 +4792,7 @@ of "typedesc"-ness is stripped off:
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:
.. code-block:: nim
@@ -5930,7 +5940,7 @@ noSideEffect pragma
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
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
error to mark a proc/iterator to have no side effect if the compiler cannot
verify this.
@@ -6593,7 +6603,7 @@ during semantic analysis:
Emit pragma
-----------
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
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
{.experimental: "notnil"}
type
PObject = ref TObj 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.
"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
parameter is any parameter that is not of type ``var``.
at runtime. An "output parameter" is a parameter of type ``var T``,
an input parameter is any parameter that is not of type ``var``.
1. Two output parameters should never 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.} =
## Returns ``LineInfo`` of ``n``, using absolute path for ``filename``.
result.filename = n.getFile
result.line = n.getLine
result.column = n.getColumn
result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn)
proc lineInfo*(arg: NimNode): string {.compileTime.} =
## 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.
##
## See also `repr`, `lispRepr`, and `astGenRepr`.
result = ""
n.treeTraverse(result, isLisp = false, indented = true)
proc lispRepr*(n: NimNode; indented = false): string {.compileTime, benign.} =
## Convert the AST ``n`` to a human-readable lisp-like string.
##
## See also ``repr``, ``treeRepr``, and ``astGenRepr``.
result = ""
n.treeTraverse(result, isLisp = true, indented = indented)
proc astGenRepr*(n: NimNode): string {.compileTime, benign.} =
@@ -1611,6 +1611,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
## assert(o.getCustomPragmaVal(serializationKey) == "mo")
## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
result = nil
let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
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:
return -1
result = 0
if (len and (len - 1)) == 0:
# when `len` is a power of 2, a faster shr can be used.
var step = len shr 1

View File

@@ -395,6 +395,8 @@ proc firstSetBitNim(x: uint64): int {.inline, noSideEffect.} =
if k == 0:
k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32
result = 32
else:
result = 0
result += firstSetBitNim(k)
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):
return path
var f, b: PathIter
var f = default PathIter
var b = default PathIter
var ff = (0, -1)
var bb = (0, -1) # (int, int)
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\""
# can't use `map` pending https://github.com/nim-lang/Nim/issues/8303
result = ""
for i in 0..<args.len:
if i > 0: result.add " "
result.add quoteShell(args[i])
@@ -2981,7 +2983,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW
setLen(result, L)
break
elif defined(macosx):
var size: cuint32
var size = cuint32(0)
getExecPath1(nil, size)
result = newString(int(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", 5) == 1
doAssert skipWhitespace("Hello World", 5) == 2
result = 0
while start+result < s.len and s[start+result] in Whitespace: inc(result)
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("CAPlow", "CAP", 0) == 3
doAssert skip("CAPlow", "cap", 0) == 0
result = 0
while start+result < s.len and result < token.len and
s[result+start] == token[result]:
inc(result)
@@ -270,6 +272,7 @@ proc skipIgnoreCase*(s, token: string, start = 0): int =
runnableExamples:
doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
result = 0
while start+result < s.len and result < token.len and
toLower(s[result+start]) == toLower(token[result]): inc(result)
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'}, 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)
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", 'W', 0) == 6
doAssert skipUntil("Hello World", 'w', 0) == 11
result = 0
while start+result < s.len and s[result+start] != until: inc(result)
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", {'e'}) == 0
doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
result = 0
while start+result < s.len and s[result+start] in toSkip: inc(result)
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
doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
doAssert res == 9223372036854775807
var res: BiggestInt
var res = BiggestInt(0)
# use 'res' for exception safety (don't write to 'number' in case of an
# overflow exception):
result = rawParseInt(s, res, start)
@@ -455,7 +461,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {.
doAssert res == 2019
doAssert parseInt("2019", res, 2) == 2
doAssert res == 19
var res: BiggestInt
var res = BiggestInt(0)
result = parseBiggestInt(s, res, start)
when sizeof(int) <= 4:
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 parseBiggestUInt("1111111111111111111", res, 0) == 19
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
# overflow exception):
result = rawParseUInt(s, res, start)
@@ -536,7 +542,7 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {.
doAssert res == 3450
doAssert parseUInt("3450", res, 2) == 2
doAssert res == 50
var res: BiggestUInt
var res = BiggestUInt(0)
result = parseBiggestUInt(s, res, start)
when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
if res > 0xFFFF_FFFF'u64:
@@ -563,7 +569,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
doAssert res == 32.57
doAssert parseFloat("32.57", res, 3) == 2
doAssert res == 57.00
var bf: BiggestFloat
var bf = BiggestFloat(0.0)
result = parseBiggestFloat(s, bf, start)
if result != 0:
number = bf

View File

@@ -44,7 +44,7 @@ proc next*(it: var PathIter; x: string): (int, int) =
it.notFirst = true
iterator dirs(x: string): (int, int) =
var it: PathIter
var it = default PathIter
while hasNext(it, x): yield next(it, x)
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.
runnableExamples:
doAssert parseInt("-0042") == -42
result = 0
let L = parseutils.parseInt(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid integer: " & s)
@@ -1112,6 +1113,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect,
## Parses a decimal integer value contained in `s`.
##
## If `s` is not a valid integer, `ValueError` is raised.
result = BiggestInt(0)
let L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len or L == 0:
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`.
##
## If `s` is not a valid integer, `ValueError` is raised.
result = uint(0)
let L = parseutils.parseUInt(s, result, 0)
if L != s.len or L == 0:
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`.
##
## If `s` is not a valid integer, `ValueError` is raised.
result = BiggestUInt(0)
let L = parseutils.parseBiggestUInt(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid unsigned integer: " & s)
@@ -1143,6 +1147,7 @@ proc parseFloat*(s: string): float {.noSideEffect,
runnableExamples:
doAssert parseFloat("3.14") == 3.14
doAssert parseFloat("inf") == 1.0/0
result = 0.0
let L = parseutils.parseFloat(s, result, 0)
if L != s.len or L == 0:
raise newException(ValueError, "invalid float: " & s)
@@ -1161,6 +1166,7 @@ proc parseBinInt*(s: string): int {.noSideEffect,
doAssert a.parseBinInt() == 53
doAssert b.parseBinInt() == 7
result = 0
let L = parseutils.parseBin(s, result, 0)
if L != s.len or L == 0:
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
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
## `s` are ignored.
result = 0
let L = parseutils.parseOct(s, result, 0)
if L != s.len or L == 0:
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
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
## within `s` are ignored.
result = 0
let L = parseutils.parseHex(s, result, 0)
if L != s.len or L == 0:
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,
bindSym"nimIdentNormalize"))
# stores all processed field strings to give error msg for ambiguous enums
var foundFields: seq[string]
var fStr: string # string of current field
var fNum: BiggestInt # int value of current field
var foundFields: seq[string] = @[]
var fStr = "" # string of current field
var fNum = BiggestInt(0) # int value of current field
for f in impl:
case f.kind
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:
return -1
let last = if last == -1: s.high else: last
result = 0
for i in countdown(last - sub.len + 1, start):
for j in 0..sub.len-1:
result = i
@@ -2044,6 +2053,7 @@ proc count*(s: string, sub: char): int {.noSideEffect,
##
## See also:
## * `countLines proc<#countLines,string>`_
result = 0
for c in s:
if c == sub: inc result
@@ -2054,6 +2064,7 @@ proc count*(s: string, subs: set[char]): int {.noSideEffect,
## See also:
## * `countLines proc<#countLines,string>`_
doAssert card(subs) > 0
result = 0
for c in s:
if c in subs: inc result
@@ -2066,6 +2077,7 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {.
## See also:
## * `countLines proc<#countLines,string>`_
doAssert sub.len > 0
result = 0
var i = 0
while true:
i = s.find(sub, i)
@@ -2313,7 +2325,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
case s[i+1]:
of 'x':
inc i, 2
var c: int
var c = 0
i += parseutils.parseHex(s, c, i, maxLen = 2)
result.add(chr(c))
dec i, 2
@@ -2514,7 +2526,7 @@ proc formatSize*(bytes: int64,
xb: int64 = bytes
fbytes: float
lastXb: int64 = bytes
matchedIndex: int
matchedIndex = 0
prefixes: array[9, string]
if prefix == bpColloquial:
prefixes = collPrefixes

View File

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

View File

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

View File

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

View File

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

View File

@@ -1767,6 +1767,7 @@ export iterators
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
## appropriate `items` and `==` operations to work.
result = 0
for i in items(a):
if i == item: return
inc(result)
@@ -2105,7 +2106,7 @@ when not defined(js):
# Linux 64bit system. -- That's because the stack direction is the other
# way around.
when declared(nimGC_setStackBottom):
var locals {.volatile.}: pointer
var locals {.volatile, noinit.}: pointer
locals = addr(locals)
nimGC_setStackBottom(locals)
@@ -2274,7 +2275,9 @@ when notJSnotNims:
of 2: d = uint(cast[ptr uint16](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))[])
else: sysAssert(false, "getDiscriminant: invalid n.typ.size")
else:
d = 0'u
sysAssert(false, "getDiscriminant: invalid n.typ.size")
return d
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 =
sysAssert(size > 0, "getBigChunk 2")
var size = size # roundup(size, PageSize)
var fl, sl: int
var fl = 0
var sl = 0
mappingSearch(size, fl, sl)
sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
result = findSuitableBlock(a, fl, sl)

View File

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

View File

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

View File

@@ -424,12 +424,12 @@ proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} =
else: write(f, "false")
proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} =
var buffer: array[65, char]
var buffer {.noinit.}: array[65, char]
discard writeFloatToBuffer(buffer, r)
if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f)
proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} =
var buffer: array[65, char]
var buffer {.noinit.}: array[65, char]
discard writeFloatToBuffer(buffer, r)
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,
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`.
##
## 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
# POSIX. We do not want to handle directories as regular files that can
# be opened.
var res: Stat
var res {.noinit.}: Stat
if c_fstat(getFileHandle(f2), res) >= 0'i32 and modeIsDir(res.st_mode):
close(f2)
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
## this inside a compile time macro you can use `staticRead
## <system.html#staticRead,string>`_.
var f: File
var f: File = nil
if open(f, filename):
try:
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
## `content` completely to the file and closes the file afterwards.
## Raises an IO exception in case of an error.
var f: File
var f: File = nil
if open(f, filename, fmWrite):
try:
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
## `content` completely to the file and closes the file afterwards.
## Raises an IO exception in case of an error.
var f: File
var f: File = nil
if open(f, filename, fmWrite):
try:
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.
## 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.
var f: File
var f: File = nil
if open(f, filename):
try:
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 8: u = cast[ptr uint64](p)[]
else:
u = uint64(0)
var a = cast[PByteArray](p)
for i in 0 .. typ.size*8-1:
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.} =
var i = 0
result = 0
when defined(x86) or defined(amd64):
while i < len - 8:
inc(result, countBits64((cast[ptr uint64](s[i].unsafeAddr))[]))

View File

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

View File

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

View File

@@ -6,8 +6,8 @@ discard """
const expected = """
tassert_c.nim(35) tassert_c
tassert_c.nim(34) foo
assertions.nim(29) failedAssertImpl
assertions.nim(22) raiseAssert
assertions.nim(30) failedAssertImpl
assertions.nim(23) raiseAssert
fatal.nim(49) sysFatal"""
proc tmatch(x, p: string): bool =
@@ -36,4 +36,5 @@ try:
except AssertionDefect:
let e = getCurrentException()
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 """
nimout: "Warning: 'y' might not have been initialized [Uninit]"
nimout: "Warning: use explicit initialization of 'y' for clarity [Uninit]"
line:34
action: compile
"""