summary refs log tree commit diff stats
path: root/compiler
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 /compiler
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
Diffstat (limited to 'compiler')
-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
27 files changed, 210 insertions, 193 deletions
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 != {}: