diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2020-06-23 10:53:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-23 10:53:57 +0200 |
commit | da29222f86f7689227ffe12605842d18c9bf0fc1 (patch) | |
tree | 312152d96e2313a81170c772ff7b51475299f344 | |
parent | a9eee6db65e72e0e11cbf5faf0794b1b6ac8bd0c (diff) | |
download | Nim-da29222f86f7689227ffe12605842d18c9bf0fc1.tar.gz |
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
53 files changed, 355 insertions, 257 deletions
diff --git a/changelog.md b/changelog.md index 151eafcd5..9d1454dcb 100644 --- a/changelog.md +++ b/changelog.md @@ -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. diff --git a/compiler/ast.nim b/compiler/ast.nim index 9639f52d3..66a1a253b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 3dd2b54d4..80f02b6c3 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -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) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2850ab750..a59529021 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -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: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 199d5c918..11743499d 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index a0ec31ac6..c393b2c81 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -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]): diff --git a/compiler/guards.nim b/compiler/guards.nim index e4f87bae4..a6ca44978 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -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 = diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 99d2bc4f2..57a6b8094 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -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") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index bc3c51d53..6287e21aa 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -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", diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 575bfe8aa..5e75c36de 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -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) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index a77fa84d3..8e733bdf8 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -21,3 +21,7 @@ define:useStdoutAsStdmsg #define:useNodeIds #gc:markAndSweep + +@if nimHasWarningObservableStores: + warning[ObservableStores]: off +@end diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 3876a114e..8683604af 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -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 = diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index e97631b77..ce9a855d8 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -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 diff --git a/compiler/parser.nim b/compiler/parser.nim index 868054f36..ccb7ca3b1 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -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) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 6c953dc18..4b8c78cc7 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -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) diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index d20ee1549..2dce6824c 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -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]) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 665eb4ea4..405de52ee 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 200d1d60a..6f267b4eb 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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! diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 438f2dbc7..e7a964d81 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -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]) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 682e74440..f8639b000 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -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.") diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 6808b1b9b..49fa44ca2 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6ffaa9dda..5875a3b68 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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 diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index eba8dd397..5407b9435 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e99269c41..e663f96b6 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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: diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 8b1f89e15..9d837b993 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -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}: diff --git a/compiler/transf.nim b/compiler/transf.nim index bd9f567ed..10a2680ae 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -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: diff --git a/compiler/types.nim b/compiler/types.nim index aaaa8e99f..b8f58c950 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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, diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index 3db60a19a..44ef3a937 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -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 != {}: diff --git a/doc/manual.rst b/doc/manual.rst index a7bca7616..da50e2c56 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -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. diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 1398d1a9b..d8efddf94 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -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. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 1b0986d33..5be3a2cce 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -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: diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 4a6c2510a..a277d1429 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -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 diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 7dd171a38..4f22388f3 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -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.} = diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bb09c966f..dcb63458b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -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): diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index c358005f1..baf238843 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -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 diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim index 8763ba878..03a65bd60 100644 --- a/lib/pure/pathnorm.nim +++ b/lib/pure/pathnorm.nim @@ -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 = diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 7c00de2d2..1ffc8bf22 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -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 diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7d82733e1..fb2f5e430 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -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 diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 9a215a353..317376405 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -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 diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index b550b0eac..8839a7767 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -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) diff --git a/lib/std/stackframes.nim b/lib/std/stackframes.nim index dbd866536..28be7ce11 100644 --- a/lib/std/stackframes.nim +++ b/lib/std/stackframes.nim @@ -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 diff --git a/lib/system.nim b/lib/system.nim index 2235b8b70..822454626 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -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 = diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 7ace0d536..95658c49a 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -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) diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim index ca3bd7bc1..c6283c89c 100644 --- a/lib/system/assertions.nim +++ b/lib/system/assertions.nim @@ -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) # --------------------------------------------------------------------------- diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index d75ada791..7f6c41e1b 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -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) diff --git a/lib/system/io.nim b/lib/system/io.nim index d1a7f1fc7..482057214 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -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) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 526839aa2..318e95ebb 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -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: diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 0df50957d..42c448848 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -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))[])) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index c3ac5fc1b..cb26833ac 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -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 diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 3048de82a..a7a514446 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -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 diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim index cbb565f84..c6a2eadb1 100644 --- a/tests/assert/tassert_c.nim +++ b/tests/assert/tassert_c.nim @@ -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 diff --git a/tests/effects/toutparam.nim b/tests/effects/toutparam.nim new file mode 100644 index 000000000..00572aa6b --- /dev/null +++ b/tests/effects/toutparam.nim @@ -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() diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index 67c0c4d8b..fe91733ff 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -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 """ |