summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2020-06-23 10:53:57 +0200
committerGitHub <noreply@github.com>2020-06-23 10:53:57 +0200
commitda29222f86f7689227ffe12605842d18c9bf0fc1 (patch)
tree312152d96e2313a81170c772ff7b51475299f344
parenta9eee6db65e72e0e11cbf5faf0794b1b6ac8bd0c (diff)
downloadNim-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
-rw-r--r--changelog.md10
-rw-r--r--compiler/ast.nim12
-rw-r--r--compiler/ccgcalls.nim14
-rw-r--r--compiler/ccgexprs.nim12
-rw-r--r--compiler/ccgtypes.nim2
-rw-r--r--compiler/dfa.nim6
-rw-r--r--compiler/guards.nim2
-rw-r--r--compiler/jsgen.nim2
-rw-r--r--compiler/lineinfos.nim2
-rw-r--r--compiler/lowerings.nim2
-rw-r--r--compiler/nim.cfg4
-rw-r--r--compiler/nimsets.nim32
-rw-r--r--compiler/parampatterns.nim6
-rw-r--r--compiler/parser.nim6
-rw-r--r--compiler/renderer.nim8
-rw-r--r--compiler/renderverbatim.nim1
-rw-r--r--compiler/semcall.nim2
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/semmagic.nim3
-rw-r--r--compiler/semobjconstr.nim2
-rw-r--r--compiler/sempass2.nim219
-rw-r--r--compiler/semstmts.nim18
-rw-r--r--compiler/semtypes.nim8
-rw-r--r--compiler/sigmatch.nim12
-rw-r--r--compiler/spawn.nim4
-rw-r--r--compiler/transf.nim10
-rw-r--r--compiler/types.nim2
-rw-r--r--compiler/writetracking.nim6
-rw-r--r--doc/manual.rst32
-rw-r--r--doc/manual_experimental.rst6
-rw-r--r--lib/core/macros.nim7
-rw-r--r--lib/pure/algorithm.nim1
-rw-r--r--lib/pure/bitops.nim2
-rw-r--r--lib/pure/os.nim6
-rw-r--r--lib/pure/parseutils.nim16
-rw-r--r--lib/pure/pathnorm.nim2
-rw-r--r--lib/pure/strutils.nim22
-rw-r--r--lib/pure/times.nim18
-rw-r--r--lib/pure/typetraits.nim2
-rw-r--r--lib/pure/unicode.nim1
-rw-r--r--lib/std/stackframes.nim6
-rw-r--r--lib/system.nim7
-rw-r--r--lib/system/alloc.nim3
-rw-r--r--lib/system/assertions.nim1
-rw-r--r--lib/system/gc_common.nim5
-rw-r--r--lib/system/io.nim16
-rw-r--r--lib/system/repr.nim1
-rw-r--r--lib/system/sets.nim1
-rw-r--r--lib/system/strmantle.nim6
-rw-r--r--lib/system/widestrs.nim1
-rw-r--r--tests/assert/tassert_c.nim7
-rw-r--r--tests/effects/toutparam.nim28
-rw-r--r--tests/init/tuninit1.nim2
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
 """