summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim26
-rw-r--r--compiler/astalgo.nim2
-rw-r--r--compiler/ccgcalls.nim4
-rw-r--r--compiler/ccgexprs.nim37
-rw-r--r--compiler/ccgmerge.nim2
-rw-r--r--compiler/ccgstmts.nim12
-rw-r--r--compiler/cgen.nim34
-rw-r--r--compiler/cgendata.nim1
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/condsyms.nim9
-rw-r--r--compiler/docgen.nim15
-rw-r--r--compiler/idents.nim37
-rw-r--r--compiler/jsgen.nim5
-rw-r--r--compiler/lineinfos.nim16
-rw-r--r--compiler/lookups.nim4
-rw-r--r--compiler/modulegraphs.nim54
-rw-r--r--compiler/msgs.nim8
-rw-r--r--compiler/nimconf.nim6
-rw-r--r--compiler/options.nim53
-rw-r--r--compiler/parampatterns.nim2
-rw-r--r--compiler/parser.nim3
-rw-r--r--compiler/patterns.nim13
-rw-r--r--compiler/pragmas.nim2
-rw-r--r--compiler/renderer.nim16
-rw-r--r--compiler/rodutils.nim21
-rw-r--r--compiler/ropes.nim26
-rw-r--r--compiler/scriptconfig.nim9
-rw-r--r--compiler/sem.nim9
-rw-r--r--compiler/semcall.nim6
-rw-r--r--compiler/semdata.nim7
-rw-r--r--compiler/semfold.nim8
-rw-r--r--compiler/seminst.nim7
-rw-r--r--compiler/semmacrosanity.nim2
-rw-r--r--compiler/semobjconstr.nim4
-rw-r--r--compiler/semstmts.nim191
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/semtypinst.nim4
-rw-r--r--compiler/sigmatch.nim24
-rw-r--r--compiler/transf.nim11
-rw-r--r--compiler/types.nim11
-rw-r--r--compiler/vm.nim30
-rw-r--r--compiler/vmdef.nim7
-rw-r--r--compiler/vmgen.nim17
43 files changed, 456 insertions, 303 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index a61ac055e..a722f63f6 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -591,7 +591,7 @@ type
     mAddI, mSubI, mMulI, mDivI, mModI,
     mSucc, mPred,
     mAddF64, mSubF64, mMulF64, mDivF64,
-    mShrI, mShlI, mBitandI, mBitorI, mBitxorI,
+    mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI,
     mMinI, mMaxI,
     mMinF64, mMaxF64,
     mAddU, mSubU, mMulU, mDivU, mModU,
@@ -972,8 +972,8 @@ const
     tyFloat..tyFloat128, tyUInt..tyUInt64}
   ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
                                     tyTuple, tySequence}
-  NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
-    tyProc, tyString, tyError}
+  NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
+    tyProc, tyError}
   ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
     skIterator,
     skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
@@ -1151,7 +1151,10 @@ proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
   for i in countup(0, high(src.data)): dest.data[i] = src.data[i]
 
 proc discardSons*(father: PNode) =
-  father.sons = nil
+  when defined(nimNoNilSeqs):
+    father.sons = @[]
+  else:
+    father.sons = nil
 
 proc withInfo*(n: PNode, info: TLineInfo): PNode =
   n.info = info
@@ -1368,7 +1371,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
   result.loc = s.loc
   result.annex = s.annex
   # XXX once usedGenerics is used, ensure module aliases keep working!
-  assert s.usedGenerics == nil
+  assert s.usedGenerics.len == 0
 
 proc initStrTable*(x: var TStrTable) =
   x.counter = 0
@@ -1593,7 +1596,10 @@ proc getStr*(a: PNode): string =
   of nkStrLit..nkTripleStrLit: result = a.strVal
   of nkNilLit:
     # let's hope this fixes more problems than it creates:
-    result = nil
+    when defined(nimNoNilSeqs):
+      result = ""
+    else:
+      result = nil
   else:
     doAssert false, "getStr"
     #internalError(a.info, "getStr")
@@ -1675,6 +1681,14 @@ proc skipStmtList*(n: PNode): PNode =
   else:
     result = n
 
+proc toVar*(typ: PType): 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)
+    rawAddSon(result, typ)
+
 proc toRef*(typ: PType): PType =
   ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
   ## returned. Otherwise ``typ`` is simply returned as-is.
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 333376f6a..152802ba1 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -412,6 +412,8 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int;
         else:
           addf(result, ",$N$1\"ident\": null", [istr])
       else:
+        if renderType and n.typ != nil:
+          addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.typ, 2)])
         if sonsLen(n) > 0:
           addf(result, ",$N$1\"sons\": [", [istr])
           for i in countup(0, sonsLen(n) - 1):
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index ab15b9f2f..83461350b 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -435,7 +435,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   assert(sonsLen(typ) == sonsLen(typ.n))
   # don't call '$' here for efficiency:
   let pat = ri.sons[0].sym.loc.r.data
-  internalAssert p.config, pat != nil
+  internalAssert p.config, pat.len > 0
   if pat.contains({'#', '(', '@', '\''}):
     var pl = genPatternCall(p, ri, pat, typ)
     # simpler version of 'fixupCall' that works with the pl+params combination:
@@ -484,7 +484,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
 
   # don't call '$' here for efficiency:
   let pat = ri.sons[0].sym.loc.r.data
-  internalAssert p.config, pat != nil
+  internalAssert p.config, pat.len > 0
   var start = 3
   if ' ' in pat:
     start = 1
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 5af0fe4e0..65cae8866 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -65,9 +65,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     of tyString:
       # with the new semantics for 'nil' strings, we can map "" to nil and
       # save tons of allocations:
-      #if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
-      #else:
-      result = genStringLiteral(p.module, n)
+      if n.strVal.len == 0 and optNilSeqs notin p.options:
+        result = genNilStringLiteral(p.module, n.info)
+      else:
+        result = genStringLiteral(p.module, n)
     else:
       if n.strVal.isNil: result = rope("NIM_NIL")
       else: result = makeCString(n.strVal)
@@ -567,9 +568,9 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "(($4)($1) - ($4)($2))", # SubF64
       "(($4)($1) * ($4)($2))", # MulF64
       "(($4)($1) / ($4)($2))", # DivF64
-
       "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
       "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
+      "($4)((NI$3)($1) >> (NU$3)($2))", # AshrI
       "($4)($1 & $2)",      # BitandI
       "($4)($1 | $2)",      # BitorI
       "($4)($1 ^ $2)",      # BitxorI
@@ -996,7 +997,7 @@ proc genEcho(p: BProc, n: PNode) =
   # is threadsafe.
   internalAssert p.config, n.kind == nkBracket
   if p.config.target.targetOS == osGenode:
-    # bypass libc and print directly to the Genode LOG session
+    # echo directly to the Genode LOG session
     var args: Rope = nil
     var a: TLoc
     for it in n.sons:
@@ -1004,8 +1005,9 @@ proc genEcho(p: BProc, n: PNode) =
         add(args, ", \"\"")
       else:
         initLocExpr(p, it, a)
-        add(args, ropecg(p.module, ", #nimToCStringConv($1)", [rdLoc(a)]))
+        add(args, ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)]))
     p.module.includeHeader("<base/log.h>")
+    p.module.includeHeader("<util/string.h>")
     linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
   else:
     if n.len == 0:
@@ -1112,8 +1114,8 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
     initLoc(call, locCall, e, OnHeap)
     call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)])
     genAssignment(p, dest, call, {})
+    gcUsage(p.config, e)
   add(p.s(cpsStmts), appends)
-  gcUsage(p.config, e)
 
 proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   # seq &= x  -->
@@ -1201,7 +1203,7 @@ proc genNew(p: BProc, e: PNode) =
     rawGenNew(p, a, nil)
   gcUsage(p.config, e)
 
-proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
+proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
   let seqtype = skipTypes(dest.t, abstractVarRange)
   let args = [getTypeDesc(p.module, seqtype),
               genTypeInfo(p.module, seqtype, dest.lode.info), length]
@@ -1212,10 +1214,14 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) =
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc)
     else:
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc)
-    call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
-    linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
+    if not lenIsZero:
+      call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args)
+      linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc)
   else:
-    call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
+    if lenIsZero:
+      call.r = rope"NIM_NIL"
+    else:
+      call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args)
     genAssignment(p, dest, call, {})
 
 proc genNewSeq(p: BProc, e: PNode) =
@@ -1228,7 +1234,9 @@ proc genNewSeq(p: BProc, e: PNode) =
       a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
       getSeqPayloadType(p.module, seqtype))
   else:
-    genNewSeqAux(p, a, b.rdLoc)
+    let lenIsZero = optNilSeqs notin p.options and
+      e[2].kind == nkIntLit and e[2].intVal == 0
+    genNewSeqAux(p, a, b.rdLoc, lenIsZero)
     gcUsage(p.config, e)
 
 proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
@@ -1328,7 +1336,8 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
   elif d.k == locNone:
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
-  genNewSeqAux(p, dest[], intLiteral(sonsLen(n)))
+  genNewSeqAux(p, dest[], intLiteral(sonsLen(n)),
+    optNilSeqs notin p.options and n.len == 0)
   for i in countup(0, sonsLen(n) - 1):
     initLoc(arr, locExpr, n[i], OnHeap)
     arr.r = ropecg(p.module, "$1$3[$2]", rdLoc(dest[]), intLiteral(i), dataField(p))
@@ -1351,7 +1360,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
     getTemp(p, n.typ, d)
   # generate call to newSeq before adding the elements per hand:
   let L = int(lengthOrd(p.config, n.sons[1].typ))
-  genNewSeqAux(p, d, intLiteral(L))
+  genNewSeqAux(p, d, intLiteral(L), optNilSeqs notin p.options and L == 0)
   initLocExpr(p, n.sons[1], a)
   # bug #5007; do not produce excessive C source code:
   if L < 10:
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 664f89b73..067a60c57 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -58,7 +58,7 @@ proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
 
 proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
   if compilationCachePresent(conf):
-    result = rope(nil)
+    result = rope("")
     add(result, "\n/*\t")
     add(result, CProcSectionNames[ps])
     add(result, ":*/\n")
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 9a8d3bcd3..69e6558bb 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -256,7 +256,15 @@ proc genSingleVar(p: BProc, a: PNode) =
     # That's why we are doing the construction inside the preInitProc.
     # genObjectInit relies on the C runtime's guarantees that
     # global variables will be initialized to zero.
-    genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true)
+    var loc = v.loc
+
+    # When the native TLS is unavailable, a global thread-local variable needs
+    # one more layer of indirection in order to access the TLS block.
+    # Only do this for complex types that may need a call to `objectInit`
+    if sfThread in v.flags and emulatedThreadVars(p.config) and
+      isComplexValueType(v.typ):
+      initLocExprSingleUse(p.module.preInitProc, vn, loc)
+    genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true)
     # Alternative construction using default constructor (which may zeromem):
     # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
     if sfExportc in v.flags and p.module.g.generatedHeader != nil:
@@ -1130,8 +1138,8 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
       patchAsgnStmtListExpr(patchedTree, e, ri)
       genStmts(p, patchedTree)
       return
-
     var a: TLoc
+    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs))
     if le.kind in {nkDerefExpr, nkHiddenDeref}:
       genDeref(p, le, a, enforceDeref=true)
     else:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 80c03f9e4..2cb431ff9 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1147,12 +1147,29 @@ proc genInitCode(m: BModule) =
     appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
           [m.nimTypesName, rope(m.nimTypes)])
 
+  # Give this small function its own scope
+  addf(prc, "{$N", [])
+  block:
+    # Keep a bogus frame in case the code needs one
+    add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
+
+    add(prc, genSectionStart(cpsLocals, m.config))
+    add(prc, m.preInitProc.s(cpsLocals))
+    add(prc, genSectionEnd(cpsLocals, m.config))
+
+    add(prc, genSectionStart(cpsInit, m.config))
+    add(prc, m.preInitProc.s(cpsInit))
+    add(prc, genSectionEnd(cpsInit, m.config))
+
+    add(prc, genSectionStart(cpsStmts, m.config))
+    add(prc, m.preInitProc.s(cpsStmts))
+    add(prc, genSectionEnd(cpsStmts, m.config))
+  addf(prc, "}$N", [])
+
   add(prc, initGCFrame(m.initProc))
 
   add(prc, genSectionStart(cpsLocals, m.config))
-  add(prc, m.preInitProc.s(cpsLocals))
   add(prc, m.initProc.s(cpsLocals))
-  add(prc, m.postInitProc.s(cpsLocals))
   add(prc, genSectionEnd(cpsLocals, m.config))
 
   if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
@@ -1166,16 +1183,13 @@ proc genInitCode(m: BModule) =
       add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
 
   add(prc, genSectionStart(cpsInit, m.config))
-  add(prc, m.preInitProc.s(cpsInit))
   add(prc, m.initProc.s(cpsInit))
-  add(prc, m.postInitProc.s(cpsInit))
   add(prc, genSectionEnd(cpsInit, m.config))
 
   add(prc, genSectionStart(cpsStmts, m.config))
-  add(prc, m.preInitProc.s(cpsStmts))
   add(prc, m.initProc.s(cpsStmts))
-  add(prc, m.postInitProc.s(cpsStmts))
   add(prc, genSectionEnd(cpsStmts, m.config))
+
   if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
     add(prc, deinitFrame(m.initProc))
   add(prc, deinitGCFrame(m.initProc))
@@ -1221,11 +1235,6 @@ proc newPreInitProc(m: BModule): BProc =
   # little hack so that unique temporaries are generated:
   result.labels = 100_000
 
-proc newPostInitProc(m: BModule): BProc =
-  result = newProc(nil, m)
-  # little hack so that unique temporaries are generated:
-  result.labels = 200_000
-
 proc initProcOptions(m: BModule): TOptions =
   let opts = m.config.options
   if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
@@ -1247,7 +1256,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   result.initProc = newProc(nil, result)
   result.initProc.options = initProcOptions(result)
   result.preInitProc = newPreInitProc(result)
-  result.postInitProc = newPostInitProc(result)
   initNodeTable(result.dataCache)
   result.typeStack = @[]
   result.forwardedProcs = @[]
@@ -1258,7 +1266,6 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
   if sfSystemModule in module.flags:
     incl result.flags, preventStackTrace
     excl(result.preInitProc.options, optStackTrace)
-    excl(result.postInitProc.options, optStackTrace)
   let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
                 else: ""
   open(result.ndi, ndiName, g.config)
@@ -1276,7 +1283,6 @@ proc resetModule*(m: BModule) =
   m.initProc = newProc(nil, m)
   m.initProc.options = initProcOptions(m)
   m.preInitProc = newPreInitProc(m)
-  m.postInitProc = newPostInitProc(m)
   initNodeTable(m.dataCache)
   m.typeStack = @[]
   m.forwardedProcs = @[]
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index a526a0f00..56dbd65a2 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -147,7 +147,6 @@ type
     headerFiles*: seq[string] # needed headers to include
     typeInfoMarker*: TypeCache # needed for generating type information
     initProc*: BProc          # code for init procedure
-    postInitProc*: BProc      # code to be executed after the init proc
     preInitProc*: BProc       # code executed before the init proc
     typeStack*: TTypeSeq      # used for type generation
     dataCache*: TNodeTable
diff --git a/compiler/commands.nim b/compiler/commands.nim
index ef5a2a40f..f7c8cf9f2 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -279,6 +279,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
   of "implicitstatic": result = contains(conf.options, optImplicitStatic)
   of "patterns": result = contains(conf.options, optPatterns)
   of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
+  of "nilseqs": result = contains(conf.options, optNilSeqs)
   else: invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
@@ -497,6 +498,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     else:
       localError(conf, info, errOnOrOffExpectedButXFound % arg)
   of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info)
+  of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info)
   of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info)
   of "floatchecks":
     processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index d853ce969..0cf264ac3 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -12,6 +12,8 @@
 import
   strtabs, platform, strutils, idents
 
+from options import Feature
+
 const
   catNone = "false"
 
@@ -74,3 +76,10 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimVmExportFixed")
   defineSymbol("nimNewRuntime")
   defineSymbol("nimIncrSeqV3")
+  defineSymbol("nimAshr")
+  defineSymbol("nimNoNilSeqs")
+  defineSymbol("nimNoNilSeqs2")
+
+  defineSymbol("nimHasNilSeqs")
+  for f in low(Feature)..high(Feature):
+    defineSymbol("nimHas" & $f)
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 8d233566c..b35452365 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -191,7 +191,7 @@ proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
 proc genComment(d: PDoc, n: PNode): string =
   result = ""
   var dummyHasToc: bool
-  if n.comment != nil:
+  if n.comment.len > 0:
     renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info),
                                toLinenumber(n.info), toColumn(n.info),
                                dummyHasToc, d.options, d.conf), result)
@@ -205,7 +205,8 @@ proc genRecComment(d: PDoc, n: PNode): Rope =
         result = genRecComment(d, n.sons[i])
         if result != nil: return
   else:
-    n.comment = nil
+    when defined(nimNoNilSeqs): n.comment = ""
+    else: n.comment = nil
 
 proc getPlainDocstring(n: PNode): string =
   ## Gets the plain text docstring of a node non destructively.
@@ -215,7 +216,7 @@ proc getPlainDocstring(n: PNode): string =
   ## the concatenated ``##`` comments of the node.
   result = ""
   if n == nil: return
-  if n.comment != nil and startsWith(n.comment, "##"):
+  if startsWith(n.comment, "##"):
     result = n.comment
   if result.len < 1:
     for i in countup(0, safeLen(n)-1):
@@ -564,9 +565,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
 
   result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
                  "col": %n.info.col}
-  if comm != nil and comm != "":
+  if comm.len > 0:
     result["description"] = %comm
-  if r.buf != nil:
+  if r.buf.len > 0:
     result["code"] = %r.buf
 
 proc checkForFalse(n: PNode): bool =
@@ -634,7 +635,7 @@ proc add(d: PDoc; j: JsonNode) =
 proc generateJson*(d: PDoc, n: PNode) =
   case n.kind
   of nkCommentStmt:
-    if n.comment != nil and startsWith(n.comment, "##"):
+    if startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
       d.add %{ "comment": %stripped, "line": %n.info.line.int,
                "col": %n.info.col }
@@ -678,7 +679,7 @@ proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
 proc generateTags*(d: PDoc, n: PNode, r: var Rope) =
   case n.kind
   of nkCommentStmt:
-    if n.comment != nil and startsWith(n.comment, "##"):
+    if startsWith(n.comment, "##"):
       let stripped = n.comment.substr(2).strip
       r.add stripped
   of nkProcDef:
diff --git a/compiler/idents.nim b/compiler/idents.nim
index 0a2f2d5cf..58800b73d 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -30,14 +30,7 @@ type
     wordCounter: int
     idAnon*, idDelegator*, emptyIdent*: PIdent
 
-when false:
-  var
-    legacy: IdentCache
-
-proc resetIdentCache*() =
-  when false:
-    for i in low(legacy.buckets)..high(legacy.buckets):
-      legacy.buckets[i] = nil
+proc resetIdentCache*() = discard
 
 proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
   if a[0] != b[0]: return 1
@@ -73,11 +66,9 @@ proc cmpExact(a, b: cstring, blen: int): int =
   if result == 0:
     if a[i] != '\0': result = 1
 
-{.this: self.}
-
-proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
-  var idx = h and high(buckets)
-  result = buckets[idx]
+proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
+  var idx = h and high(ic.buckets)
+  result = ic.buckets[idx]
   var last: PIdent = nil
   var id = 0
   while result != nil:
@@ -85,8 +76,8 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
       if last != nil:
         # make access to last looked up identifier faster:
         last.next = result.next
-        result.next = buckets[idx]
-        buckets[idx] = result
+        result.next = ic.buckets[idx]
+        ic.buckets[idx] = result
       return
     elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
       assert((id == 0) or (id == result.id))
@@ -97,20 +88,20 @@ proc getIdent*(self: IdentCache; identifier: cstring, length: int, h: Hash): PId
   result.h = h
   result.s = newString(length)
   for i in countup(0, length - 1): result.s[i] = identifier[i]
-  result.next = buckets[idx]
-  buckets[idx] = result
+  result.next = ic.buckets[idx]
+  ic.buckets[idx] = result
   if id == 0:
-    inc(wordCounter)
-    result.id = -wordCounter
+    inc(ic.wordCounter)
+    result.id = -ic.wordCounter
   else:
     result.id = id
 
-proc getIdent*(self: IdentCache; identifier: string): PIdent =
-  result = getIdent(cstring(identifier), len(identifier),
+proc getIdent*(ic: IdentCache; identifier: string): PIdent =
+  result = getIdent(ic, cstring(identifier), len(identifier),
                     hashIgnoreStyle(identifier))
 
-proc getIdent*(self: IdentCache; identifier: string, h: Hash): PIdent =
-  result = getIdent(cstring(identifier), len(identifier), h)
+proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent =
+  result = getIdent(ic, cstring(identifier), len(identifier), h)
 
 proc newIdentCache*(): IdentCache =
   result = IdentCache()
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index ef54841ae..462c622aa 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -379,6 +379,7 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
     ["", "", "($1 / $2)", "($1 / $2)"], # DivF64
     ["", "", "", ""], # ShrI
     ["", "", "($1 << $2)", "($1 << $2)"], # ShlI
+    ["", "", "($1 >> $2)", "($1 >> $2)"], # AshrI
     ["", "", "($1 & $2)", "($1 & $2)"], # BitandI
     ["", "", "($1 | $2)", "($1 | $2)"], # BitorI
     ["", "", "($1 ^ $2)", "($1 ^ $2)"], # BitxorI
@@ -1266,7 +1267,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   if f.loc.r == nil: f.loc.r = mangleName(p.module, f)
   if sfInfixCall in f.flags:
     let pat = n.sons[0].sym.loc.r.data
-    internalAssert p.config, pat != nil
+    internalAssert p.config, pat.len > 0
     if pat.contains({'#', '(', '@'}):
       var typ = skipTypes(n.sons[0].typ, abstractInst)
       assert(typ.kind == tyProc)
@@ -1349,7 +1350,7 @@ proc arrayTypeForElemType(typ: PType): string =
   of tyUint8: "Uint8Array"
   of tyFloat32: "Float32Array"
   of tyFloat64, tyFloat: "Float64Array"
-  else: nil
+  else: ""
 
 proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   var t = skipTypes(typ, abstractInst)
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index 261dcb44e..c5a641713 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -92,7 +92,7 @@ const
     warnResultShadowed: "Special variable 'result' is shadowed.",
     warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
     warnUser: "$1",
-    hintSuccess: "operation successful",
+    hintSuccess: "operation successful: $#",
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
     hintCC: "CC: \'$1\'", # unused
     hintLineTooLong: "line too long",
@@ -164,13 +164,13 @@ type
   TNoteKinds* = set[TNoteKind]
 
 proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
-    result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
-    result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
-    result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
-      warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
-      hintSource, hintGlobalVar, hintGCStats}
-    result[0] = result[1] - {hintSuccessX, hintConf, hintProcessing,
-      hintPattern, hintExecuting, hintLinking}
+  result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
+  result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
+  result[1] = result[2] - {warnShadowIdent, warnProveField, warnProveIndex,
+    warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
+    hintSource, hintGlobalVar, hintGCStats}
+  result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,
+    hintProcessing, hintPattern, hintExecuting, hintLinking}
 
 const
   NotesVerbosity* = computeNotesVerbosity()
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 87694988a..1e9d963fa 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, ast, astalgo, idents, semdata, types, msgs, options,
-  renderer, wordrecg, idgen, nimfix.prettybase, lineinfos, strutils
+  renderer, wordrecg, idgen, nimfix/prettybase, lineinfos, strutils
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -262,7 +262,7 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
     err.add "\nThis might be caused by a recursive module dependency: "
     err.add c.recursiveDep
     # prevent excessive errors for 'nim check'
-    c.recursiveDep = nil
+    c.recursiveDep = ""
   localError(c.config, info, errGenerated, err)
 
 proc lookUp*(c: PContext, n: PNode): PSym =
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 334cd1ae6..1eecc4176 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -65,10 +65,8 @@ type
 
 proc hash*(x: FileIndex): Hash {.borrow.}
 
-{.this: g.}
-
 proc stopCompile*(g: ModuleGraph): bool {.inline.} =
-  result = doStopCompile != nil and doStopCompile()
+  result = g.doStopCompile != nil and g.doStopCompile()
 
 proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
   result = newSym(skProc, getIdent(g.cache, name), nil, unknownLineInfo(), {})
@@ -98,44 +96,44 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result.cacheTables = initTable[string, BTree[string, PNode]]()
 
 proc resetAllModules*(g: ModuleGraph) =
-  initStrTable(packageSyms)
-  deps = initIntSet()
-  modules = @[]
-  importStack = @[]
-  inclToMod = initTable[FileIndex, FileIndex]()
-  usageSym = nil
-  owners = @[]
-  methods = @[]
-  initStrTable(compilerprocs)
-  initStrTable(exposed)
+  initStrTable(g.packageSyms)
+  g.deps = initIntSet()
+  g.modules = @[]
+  g.importStack = @[]
+  g.inclToMod = initTable[FileIndex, FileIndex]()
+  g.usageSym = nil
+  g.owners = @[]
+  g.methods = @[]
+  initStrTable(g.compilerprocs)
+  initStrTable(g.exposed)
 
 proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
-  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
-    result = modules[fileIdx.int32]
+  if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len:
+    result = g.modules[fileIdx.int32]
 
 proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
 
 proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
   assert m.position == m.info.fileIndex.int32
   addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false)
-  if suggestMode:
-    deps.incl m.position.dependsOn(dep.int)
+  if g.suggestMode:
+    g.deps.incl m.position.dependsOn(dep.int)
     # we compute the transitive closure later when quering the graph lazily.
     # this improves efficiency quite a lot:
     #invalidTransitiveClosure = true
 
 proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
   addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true)
-  discard hasKeyOrPut(inclToMod, includeFile, module)
+  discard hasKeyOrPut(g.inclToMod, includeFile, module)
 
 proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
   ## returns 'fileIdx' if the file belonging to this index is
   ## directly used as a module or else the module that first
   ## references this include file.
-  if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
+  if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len and g.modules[fileIdx.int32] != nil:
     result = fileIdx
   else:
-    result = inclToMod.getOrDefault(fileIdx)
+    result = g.inclToMod.getOrDefault(fileIdx)
 
 proc transitiveClosure(g: var IntSet; n: int) =
   # warshall's algorithm
@@ -147,22 +145,22 @@ proc transitiveClosure(g: var IntSet; n: int) =
             g.incl i.dependsOn(j)
 
 proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
-  let m = getModule fileIdx
+  let m = g.getModule fileIdx
   if m != nil: incl m.flags, sfDirty
 
 proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
   # we need to mark its dependent modules D as dirty right away because after
   # nimsuggest is done with this module, the module's dirty flag will be
   # cleared but D still needs to be remembered as 'dirty'.
-  if invalidTransitiveClosure:
-    invalidTransitiveClosure = false
-    transitiveClosure(deps, modules.len)
+  if g.invalidTransitiveClosure:
+    g.invalidTransitiveClosure = false
+    transitiveClosure(g.deps, g.modules.len)
 
   # every module that *depends* on this file is also dirty:
-  for i in 0i32..<modules.len.int32:
-    let m = modules[i]
-    if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
+  for i in 0i32..<g.modules.len.int32:
+    let m = g.modules[i]
+    if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
       incl m.flags, sfDirty
 
 proc isDirty*(g: ModuleGraph; m: PSym): bool =
-  result = suggestMode and sfDirty in m.flags
+  result = g.suggestMode and sfDirty in m.flags
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index be2ece911..1d7939142 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -391,10 +391,10 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) =
 
   if conf.structuredErrorHook != nil:
     conf.structuredErrorHook(conf, unknownLineInfo(),
-      s & (if kind != nil: KindFormat % kind else: ""), sev)
+      s & (if kind.len > 0: KindFormat % kind else: ""), sev)
 
   if not ignoreMsgBecauseOfIdeTools(conf, msg):
-    if kind != nil:
+    if kind.len > 0:
       styledMsgWriteln(color, title, resetStyle, s,
                        KindColor, `%`(KindFormat, kind))
     else:
@@ -483,9 +483,9 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
 
   if not ignoreMsg:
     if conf.structuredErrorHook != nil:
-      conf.structuredErrorHook(conf, info, s & (if kind != nil: KindFormat % kind else: ""), sev)
+      conf.structuredErrorHook(conf, info, s & (if kind.len > 0: KindFormat % kind else: ""), sev)
     if not ignoreMsgBecauseOfIdeTools(conf, msg):
-      if kind != nil:
+      if kind.len > 0:
         styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s,
                          KindColor, `%`(KindFormat, kind))
       else:
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index d3b4645dc..1a8a0acb5 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -220,7 +220,7 @@ proc readConfigFile(
     return true
 
 proc getUserConfigPath(filename: string): string =
-  result = joinPath(getConfigDir(), filename)
+  result = joinPath([getConfigDir(), "nim", filename])
 
 proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
   # try standard configuration file (installation did not distribute files
@@ -228,8 +228,8 @@ proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
   let p = getPrefixDir(conf)
   result = joinPath([p, "config", filename])
   when defined(unix):
-    if not existsFile(result): result = joinPath([p, "etc", filename])
-    if not existsFile(result): result = "/etc/" & filename
+    if not existsFile(result): result = joinPath([p, "etc/nim", filename])
+    if not existsFile(result): result = "/etc/nim/" & filename
 
 proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
   setDefaultLibpath(conf)
diff --git a/compiler/options.nim b/compiler/options.nim
index a776961fc..04b14c65f 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -38,7 +38,8 @@ type                          # please make sure we have under 32 options
     optPatterns,              # en/disable pattern matching
     optMemTracker,
     optHotCodeReloading,
-    optLaxStrings
+    optLaxStrings,
+    optNilSeqs
 
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
@@ -110,14 +111,16 @@ type
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
     ideHighlight, ideOutline, ideKnown, ideMsg
 
-  Feature* = enum  ## experimental features
+  Feature* = enum  ## experimental features; DO NOT RENAME THESE!
     implicitDeref,
     dotOperators,
     callOperator,
     parallel,
     destructor,
     notnil,
-    dynamicBindSym
+    dynamicBindSym,
+    forLoopMacros,
+    caseStmtMacros
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf
@@ -354,6 +357,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
     of "msdos": result = conf.target.targetOS == osDos
     of "mswindows", "win32": result = conf.target.targetOS == osWindows
     of "macintosh": result = conf.target.targetOS in {osMacos, osMacosx}
+    of "osx": result = conf.target.targetOS == osMacosx
     of "sunos": result = conf.target.targetOS == osSolaris
     of "nintendoswitch":
       result = conf.target.targetOS == osNintendoSwitch
@@ -479,9 +483,20 @@ proc disableNimblePath*(conf: ConfigRef) =
 
 include packagehandling
 
+proc getOsCacheDir(): string =
+  when defined(posix):
+    result = getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nim"
+  else:
+    result = getHomeDir() / genSubDir
+
 proc getNimcacheDir*(conf: ConfigRef): string =
-  result = if conf.nimcacheDir.len > 0: conf.nimcacheDir
-           else: shortenDir(conf, conf.projectPath) / genSubDir
+  # XXX projectName should always be without a file extension!
+  result = if conf.nimcacheDir.len > 0:
+             conf.nimcacheDir
+           elif conf.cmd == cmdCompileToJS:
+             shortenDir(conf, conf.projectPath) / genSubDir
+           else: getOsCacheDir() / splitFile(conf.projectName).name &
+             (if isDefined(conf, "release"): "_r" else: "_d")
 
 proc pathSubs*(conf: ConfigRef; p, config: string): string =
   let home = removeTrailingDirSep(os.getHomeDir())
@@ -588,18 +603,22 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
 proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
   const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
   var candidates: seq[string] = @[]
-  for k, f in os.walkDir(pkg, relative=true):
-    if k == pcFile and f != "config.nims":
-      let (_, name, ext) = splitFile(f)
-      if ext in extensions:
-        let x = changeFileExt(pkg / name, ".nim")
-        if fileExists(x):
-          candidates.add x
-  for c in candidates:
-    # nim-foo foo  or  foo  nfoo
-    if (pkg in c) or (c in pkg): return c
-  if candidates.len >= 1:
-    return candidates[0]
+  var dir = pkg
+  while true:
+    for k, f in os.walkDir(dir, relative=true):
+      if k == pcFile and f != "config.nims":
+        let (_, name, ext) = splitFile(f)
+        if ext in extensions:
+          let x = changeFileExt(dir / name, ".nim")
+          if fileExists(x):
+            candidates.add x
+    for c in candidates:
+      # nim-foo foo  or  foo  nfoo
+      if (pkg in c) or (c in pkg): return c
+    if candidates.len >= 1:
+      return candidates[0]
+    dir = parentDir(dir)
+    if dir == "": break
   return ""
 
 proc canonDynlibName(s: string): string =
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index 944aec048..bbaf7a069 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -217,7 +217,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
     if n.typ != nil and n.typ.kind == tyVar:
       result = arLValue
   of nkSym:
-    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
+    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar}
                 else: {skVar, skResult, skTemp}
     if n.sym.kind in kinds:
       if owner != nil and owner == n.sym.owner and
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 5664a9f67..98ccb05b9 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -150,7 +150,8 @@ template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
 proc rawSkipComment(p: var TParser, node: PNode) =
   if p.tok.tokType == tkComment:
     if node != nil:
-      if node.comment == nil: node.comment = ""
+      when not defined(nimNoNilSeqs):
+        if node.comment == nil: node.comment = ""
       when defined(nimpretty):
         if p.tok.commentOffsetB > p.tok.commentOffsetA:
           add node.comment, fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 2d2aeba76..ebb3a7c1d 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -21,14 +21,17 @@ type
     formals: int
     c: PContext
     subMatch: bool       # subnode matches are special
+    mappingIsFull: bool
   PPatternContext = var TPatternContext
 
 proc getLazy(c: PPatternContext, sym: PSym): PNode =
-  if not isNil(c.mapping):
+  if c.mappingIsFull:
     result = c.mapping[sym.position]
 
 proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
-  if isNil(c.mapping): newSeq(c.mapping, c.formals)
+  if not c.mappingIsFull:
+    newSeq(c.mapping, c.formals)
+    c.mappingIsFull = true
   c.mapping[sym.position] = n
 
 proc matches(c: PPatternContext, p, n: PNode): bool
@@ -209,7 +212,11 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
     for j in 0 ..< p.len:
       if not matches(c, p.sons[j], n.sons[i+j]):
         # we need to undo any bindings:
-        if not isNil(c.mapping): c.mapping = nil
+        when defined(nimNoNilSeqs):
+          c.mapping = @[]
+          c.mappingIsFull = false
+        else:
+          if not isNil(c.mapping): c.mapping = nil
         return false
     result = true
 
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index afe60e9dd..a067f2074 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -1066,8 +1066,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wThis:
         if it.kind in nkPragmaCallKinds and it.len == 2:
           c.selfName = considerQuotedIdent(c, it[1])
+          message(c.config, n.info, warnDeprecated, "the '.this' pragma")
         elif it.kind == nkIdent or it.len == 1:
           c.selfName = getIdent(c.cache, "self")
+          message(c.config, n.info, warnDeprecated, "the '.this' pragma")
         else:
           localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
       of wNoRewrite:
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index ce27e1cd9..c3e151f5a 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -281,7 +281,7 @@ const
 
 proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
   result = false
-  if n.comment != nil:
+  if n.comment.len > 0:
     result = (renderNoComments notin g.flags) or
         (renderDocComments in g.flags)
 
@@ -402,7 +402,7 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
 proc lsub(g: TSrcGen; n: PNode): int =
   # computes the length of a tree
   if isNil(n): return 0
-  if n.comment != nil: return MaxLineLen + 1
+  if n.comment.len > 0: return MaxLineLen + 1
   case n.kind
   of nkEmpty: result = 0
   of nkTripleStrLit:
@@ -500,7 +500,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkBreakStmt: result = lsub(g, n.sons[0]) + len("break_")
   of nkContinueStmt: result = lsub(g, n.sons[0]) + len("continue_")
   of nkPragma: result = lcomma(g, n) + 4
-  of nkCommentStmt: result = if n.comment.isNil: 0 else: len(n.comment)
+  of nkCommentStmt: result = len(n.comment)
   of nkOfBranch: result = lcomma(g, n, 0, - 2) + lsub(g, lastSon(n)) + len("of_:_")
   of nkImportAs: result = lsub(g, n.sons[0]) + len("_as_") + lsub(g, n.sons[1])
   of nkElifBranch: result = lsons(g, n) + len("elif_:_")
@@ -539,7 +539,7 @@ proc gsub(g: var TSrcGen, n: PNode) =
 proc hasCom(n: PNode): bool =
   result = false
   if n.isNil: return false
-  if n.comment != nil: return true
+  if n.comment.len > 0: return true
   case n.kind
   of nkEmpty..nkNilLit: discard
   else:
@@ -602,7 +602,7 @@ proc gsection(g: var TSrcGen, n: PNode, c: TContext, kind: TTokType,
   dedent(g)
 
 proc longMode(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): bool =
-  result = n.comment != nil
+  result = n.comment.len > 0
   if not result:
     # check further
     for i in countup(start, sonsLen(n) + theEnd):
@@ -637,7 +637,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
 proc gcond(g: var TSrcGen, n: PNode) =
   if n.kind == nkStmtListExpr:
     put(g, tkParLe, "(")
-  gsub(g, n)  
+  gsub(g, n)
   if n.kind == nkStmtListExpr:
     put(g, tkParRi, ")")
 
@@ -864,7 +864,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
   if isNil(n): return
   var
     a: TContext
-  if n.comment != nil: pushCom(g, n)
+  if n.comment.len > 0: pushCom(g, n)
   case n.kind                 # atoms:
   of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
   of nkEmpty: discard
@@ -1079,7 +1079,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
                 elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
                 else: nil
       var n_next = n[1]
-      while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, 
+      while n_next.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref,
                   nkStringToCString, nkCStringToString} and n_next.len > 0:
         n_next = n_next[0]
       if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 66d7f63c2..a774cdba7 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -10,6 +10,27 @@
 ## Serialization utilities for the compiler.
 import strutils, math
 
+# MSVC prior to 2013 doesn't have C99 functions
+when defined(windows) and (defined(vcc) or defined(bcc)):
+  {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900
+  #include <stdarg.h>
+  static int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) {
+    int count = -1;
+    if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+    if (count == -1) count = _vscprintf(format, ap);
+    return count;
+  }
+  int snprintf(char *outBuf, size_t size, const char *format, ...) {
+    int count;
+    va_list ap;
+    va_start(ap, format);
+    count = c99_vsnprintf(outBuf, size, format, ap);
+    va_end(ap);
+    return count;
+  }
+  #endif
+  """.}
+
 proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
 
 proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 973f16916..81ee01dbf 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -66,29 +66,19 @@ type
   Rope* = ref RopeObj
   RopeObj*{.acyclic.} = object of RootObj # the empty rope is represented
                                           # by nil to safe space
-    left*, right*: Rope
-    length*: int
-    data*: string             # != nil if a leaf
+    left, right: Rope
+    L: int                    # <= 0 if a leaf
+    data*: string
 
 proc len*(a: Rope): int =
   ## the rope's length
   if a == nil: result = 0
-  else: result = a.length
+  else: result = abs a.L
 
-proc newRope(data: string = nil): Rope =
+proc newRope(data: string = ""): Rope =
   new(result)
-  if data != nil:
-    result.length = len(data)
-    result.data = data
-
-proc newMutableRope*(capacity = 30): Rope =
-  ## creates a new rope that supports direct modifications of the rope's
-  ## 'data' and 'length' fields.
-  new(result)
-  result.data = newStringOfCap(capacity)
-
-proc freezeMutableRope*(r: Rope) {.inline.} =
-  r.length = r.data.len
+  result.L = -len(data)
+  result.data = data
 
 var
   cache: array[0..2048*2 - 1, Rope] # XXX Global here!
@@ -147,7 +137,7 @@ proc `&`*(a, b: Rope): Rope =
     result = a
   else:
     result = newRope()
-    result.length = a.length + b.length
+    result.L = abs(a.L) + abs(b.L)
     result.left = a
     result.right = b
 
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index ae7e030b8..659206a40 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -45,7 +45,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   template cbos(name, body) {.dirty.} =
     result.registerCallback "stdlib.system." & astToStr(name),
       proc (a: VmArgs) =
-        errorMsg = nil
+        errorMsg = ""
         try:
           body
         except OSError:
@@ -159,8 +159,11 @@ proc runNimScript*(cache: IdentCache; scriptName: string;
 
   defineSymbol(conf.symbols, "nimscript")
   defineSymbol(conf.symbols, "nimconfig")
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
+  var registeredPasses {.global.} = false
+  if not registeredPasses:
+    registerPass(graph, semPass)
+    registerPass(graph, evalPass)
+    registeredPasses = true
 
   conf.searchPaths.add(conf.libpath)
 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 2c3fab5a5..6128c02d1 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -16,12 +16,12 @@ import
   procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
-  semparallel, lowerings, pluginsupport, plugins.active, rod, lineinfos
+  semparallel, lowerings, pluginsupport, plugins/active, rod, lineinfos
 
 from modulegraphs import ModuleGraph
 
 when defined(nimfix):
-  import nimfix.prettybase
+  import nimfix/prettybase
 
 # implementation
 
@@ -119,7 +119,7 @@ proc commonType*(x, y: PType): PType =
   elif b.kind == tyStmt: result = b
   elif a.kind == tyTypeDesc:
     # turn any concrete typedesc into the abstract typedesc type
-    if a.sons == nil: result = a
+    if a.len == 0: result = a
     else:
       result = newType(tyTypeDesc, a.owner)
       rawAddSon(result, newType(tyNone, a.owner))
@@ -621,7 +621,8 @@ proc testExamples(c: PContext) =
   if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
     quit "[Examples] failed: see " & outp
   else:
-    removeFile(outp)
+    # keep generated source file `outp` to allow inspection.
+    rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp])
     removeFile(outp.changeFileExt(ExeExt))
     try:
       removeDir(nimcache)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index aa5394a71..ef452fcdc 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -89,7 +89,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
       continue
     determineType(c, sym)
     initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag)
-    if c.currentScope.symbols.counter == counterInitial or syms != nil:
+    if c.currentScope.symbols.counter == counterInitial or syms.len != 0:
       matches(c, n, orig, z)
       if z.state == csMatch:
         #if sym.name.s == "==" and (n.info ?? "temp3"):
@@ -237,7 +237,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
     if symx.kind in routineKinds:
       errors.add(CandidateError(sym: symx,
                                 unmatchedVarParam: 0, firstMismatch: 0,
-                                diagnostics: nil,
+                                diagnostics: @[],
                                 enabled: false))
     symx = nextOverloadIter(o, c, headSymbol)
   if errors.len == 0:
@@ -455,7 +455,7 @@ proc tryDeref(n: PNode): PNode =
 
 proc semOverloadedCall(c: PContext, n, nOrig: PNode,
                        filter: TSymKinds, flags: TExprFlags): PNode =
-  var errors: CandidateErrors = if efExplain in flags: @[] else: nil
+  var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil
   var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
   if r.state == csMatch:
     # this may be triggered, when the explain pragma is used
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index aa0cb6e8e..4189a5214 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -37,6 +37,7 @@ type
                               # in standalone ``except`` and ``finally``
     next*: PProcCon           # used for stacking procedure contexts
     wasForwarded*: bool       # whether the current proc has a separate header
+    mappingExists*: bool
     mapping*: TIdTable
 
   TMatchedConcept* = object
@@ -176,12 +177,14 @@ proc lastOptionEntry*(c: PContext): POptionEntry =
 proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
 
 proc put*(p: PProcCon; key, val: PSym) =
-  if p.mapping.data == nil: initIdTable(p.mapping)
+  if not p.mappingExists:
+    initIdTable(p.mapping)
+    p.mappingExists = true
   #echo "put into table ", key.info
   p.mapping.idTablePut(key, val)
 
 proc get*(p: PProcCon; key: PSym): PSym =
-  if p.mapping.data == nil: return nil
+  if not p.mappingExists: return nil
   result = PSym(p.mapping.idTableGet(key))
 
 proc getGenSym*(c: PContext; s: PSym): PSym =
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index d2abfac13..a6c185fdc 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -268,6 +268,14 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
     of tyInt64, tyInt, tyUInt..tyUInt64:
       result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
     else: internalError(g.config, n.info, "constant folding for shr")
+  of mAshrI:
+    case skipTypes(n.typ, abstractRange).kind
+    of tyInt8: result = newIntNodeT(ashr(int8(getInt(a)), int8(getInt(b))), n, g)
+    of tyInt16: result = newIntNodeT(ashr(int16(getInt(a)), int16(getInt(b))), n, g)
+    of tyInt32: result = newIntNodeT(ashr(int32(getInt(a)), int32(getInt(b))), n, g)
+    of tyInt64, tyInt:
+      result = newIntNodeT(ashr(getInt(a), getInt(b)), n, g)
+    else: internalError(g.config, n.info, "constant folding for ashr")
   of mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
   of mModI: result = foldMod(getInt(a), getInt(b), n, g)
   of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index fac04e3a0..f9d7c3754 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -97,10 +97,9 @@ proc sameInstantiation(a, b: TInstantiation): bool =
 
 proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
                      id: CompilesId): PSym =
-  if genericSym.procInstCache != nil:
-    for inst in genericSym.procInstCache:
-      if inst.compilesId == id and sameInstantiation(entry, inst[]):
-        return inst.sym
+  for inst in genericSym.procInstCache:
+    if inst.compilesId == id and sameInstantiation(entry, inst[]):
+      return inst.sym
 
 when false:
   proc `$`(x: PSym): string =
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index 02c56c035..3056f5d72 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -88,7 +88,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
     else:
       globalError(conf, n.info, "string literal must be of some string type")
   of nkNilLit:
-    if x.kind in NilableTypes:
+    if x.kind in NilableTypes+{tyString, tySequence}:
       n.typ = t
     else:
       globalError(conf, n.info, "nil literal must be of some pointer type")
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 8b639806d..90ab2c57a 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -121,7 +121,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
     if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
       let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
       if assignment == nil:
-        if result == nil:
+        if result.len == 0:
           result = r.sym.name.s
         else:
           result.add ", "
@@ -129,7 +129,7 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
 
 proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
   let missing = missingMandatoryFields(c, recList, initExpr)
-  if missing != nil:
+  if missing.len > 0:
     localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
 
 proc semConstructFields(c: PContext, recNode: PNode,
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f7d8b6b7b..3a1278137 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -178,71 +178,6 @@ proc semIf(c: PContext, n: PNode): PNode =
     result.kind = nkIfExpr
     result.typ = typ
 
-proc semCase(c: PContext, n: PNode): PNode =
-  result = n
-  checkMinSonsLen(n, 2, c.config)
-  openScope(c)
-  n.sons[0] = semExprWithType(c, n.sons[0])
-  var chckCovered = false
-  var covered: BiggestInt = 0
-  var typ = commonTypeBegin
-  var hasElse = false
-  let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
-  case caseTyp.kind
-  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
-    chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyError:
-    discard
-  else:
-    localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
-    return
-  for i in countup(1, sonsLen(n) - 1):
-    var x = n.sons[i]
-    when defined(nimsuggest):
-      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
-        suggestEnum(c, x, caseTyp)
-    case x.kind
-    of nkOfBranch:
-      checkMinSonsLen(x, 2, c.config)
-      semCaseBranch(c, n, x, i, covered)
-      var last = sonsLen(x)-1
-      x.sons[last] = semExprBranchScope(c, x.sons[last])
-      typ = commonType(typ, x.sons[last])
-    of nkElifBranch:
-      chckCovered = false
-      checkSonsLen(x, 2, c.config)
-      openScope(c)
-      x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      x.sons[1] = semExprBranch(c, x.sons[1])
-      typ = commonType(typ, x.sons[1])
-      closeScope(c)
-    of nkElse:
-      chckCovered = false
-      checkSonsLen(x, 1, c.config)
-      x.sons[0] = semExprBranchScope(c, x.sons[0])
-      typ = commonType(typ, x.sons[0])
-      hasElse = true
-    else:
-      illFormedAst(x, c.config)
-  if chckCovered:
-    if covered == toCover(c, n.sons[0].typ):
-      hasElse = true
-    else:
-      localError(c.config, n.info, "not all cases are covered")
-  closeScope(c)
-  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
-    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
-    # propagate any enforced VoidContext:
-    if typ == c.enforceVoidContext:
-      result.typ = c.enforceVoidContext
-  else:
-    for i in 1..n.len-1:
-      var it = n.sons[i]
-      let j = it.len-1
-      if not endsInNoReturn(it.sons[j]):
-        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
-    result.typ = typ
-
 proc semTry(c: PContext, n: PNode): PNode =
 
   var check = initIntSet()
@@ -683,29 +618,28 @@ proc isTrivalStmtExpr(n: PNode): bool =
       return false
   result = true
 
-proc handleForLoopMacro(c: PContext; n: PNode): PNode =
-  let iterExpr = n[^2]
-  if iterExpr.kind in nkCallKinds:
+proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string): PNode =
+  if selector.kind in nkCallKinds:
     # we transform
     # n := for a, b, c in m(x, y, z): Y
     # to
     # m(n)
-    let forLoopStmt = magicsys.getCompilerProc(c.graph, "ForLoopStmt")
-    if forLoopStmt == nil: return
+    let maType = magicsys.getCompilerProc(c.graph, magicType)
+    if maType == nil: return
 
-    let headSymbol = iterExpr[0]
+    let headSymbol = selector[0]
     var o: TOverloadIter
     var match: PSym = nil
     var symx = initOverloadIter(o, c, headSymbol)
     while symx != nil:
       if symx.kind in {skTemplate, skMacro}:
-        if symx.typ.len == 2 and symx.typ[1] == forLoopStmt.typ:
+        if symx.typ.len == 2 and symx.typ[1] == maType.typ:
           if match == nil:
             match = symx
           else:
             localError(c.config, n.info, errAmbiguousCallXYZ % [
               getProcHeader(c.config, match),
-              getProcHeader(c.config, symx), $iterExpr])
+              getProcHeader(c.config, symx), $selector])
       symx = nextOverloadIter(o, c, headSymbol)
 
     if match == nil: return
@@ -717,11 +651,44 @@ proc handleForLoopMacro(c: PContext; n: PNode): PNode =
     of skTemplate: result = semTemplateExpr(c, callExpr, match, {})
     else: result = nil
 
+proc handleForLoopMacro(c: PContext; n: PNode): PNode =
+  result = handleStmtMacro(c, n, n[^2], "ForLoopStmt")
+
+proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
+  # n[0] has been sem'checked and has a type. We use this to resolve
+  # 'match(n[0])' but then we pass 'n' to the 'match' macro. This seems to
+  # be the best solution.
+  var toResolve = newNodeI(nkCall, n.info)
+  toResolve.add newIdentNode(getIdent(c.cache, "match"), n.info)
+  toResolve.add n[0]
+
+  var errors: CandidateErrors
+  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {},
+                           errors, false)
+  if r.state == csMatch:
+    var match = r.calleeSym
+    markUsed(c.config, n[0].info, match, c.graph.usageSym)
+    styleCheckUse(n[0].info, match)
+
+    # but pass 'n' to the 'match' macro, not 'n[0]':
+    r.call.sons[1] = n
+    let toExpand = semResolvedCall(c, r, r.call, {})
+    case match.kind
+    of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, {})
+    of skTemplate: result = semTemplateExpr(c, toExpand, match, {})
+    else: result = nil
+  # this would be the perfectly consistent solution with 'for loop macros',
+  # but it kinda sucks for pattern matching as the matcher is not attached to
+  # a type then:
+  when false:
+    result = handleStmtMacro(c, n, n[0], "CaseStmt")
+
 proc semFor(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 3, c.config)
   var length = sonsLen(n)
-  result = handleForLoopMacro(c, n)
-  if result != nil: return result
+  if forLoopMacros in c.features:
+    result = handleForLoopMacro(c, n)
+    if result != nil: return result
   openScope(c)
   result = n
   n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
@@ -757,6 +724,75 @@ proc semFor(c: PContext, n: PNode): PNode =
     result.typ = c.enforceVoidContext
   closeScope(c)
 
+proc semCase(c: PContext, n: PNode): PNode =
+  result = n
+  checkMinSonsLen(n, 2, c.config)
+  openScope(c)
+  n.sons[0] = semExprWithType(c, n.sons[0])
+  var chckCovered = false
+  var covered: BiggestInt = 0
+  var typ = commonTypeBegin
+  var hasElse = false
+  let caseTyp = skipTypes(n.sons[0].typ, abstractVarRange-{tyTypeDesc})
+  case caseTyp.kind
+  of tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool:
+    chckCovered = true
+  of tyFloat..tyFloat128, tyString, tyError:
+    discard
+  else:
+    if caseStmtMacros in c.features:
+      result = handleCaseStmtMacro(c, n)
+      if result != nil: return result
+
+    localError(c.config, n.info, errSelectorMustBeOfCertainTypes)
+    return
+  for i in countup(1, sonsLen(n) - 1):
+    var x = n.sons[i]
+    when defined(nimsuggest):
+      if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, x.info) and caseTyp.kind == tyEnum:
+        suggestEnum(c, x, caseTyp)
+    case x.kind
+    of nkOfBranch:
+      checkMinSonsLen(x, 2, c.config)
+      semCaseBranch(c, n, x, i, covered)
+      var last = sonsLen(x)-1
+      x.sons[last] = semExprBranchScope(c, x.sons[last])
+      typ = commonType(typ, x.sons[last])
+    of nkElifBranch:
+      chckCovered = false
+      checkSonsLen(x, 2, c.config)
+      openScope(c)
+      x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
+      x.sons[1] = semExprBranch(c, x.sons[1])
+      typ = commonType(typ, x.sons[1])
+      closeScope(c)
+    of nkElse:
+      chckCovered = false
+      checkSonsLen(x, 1, c.config)
+      x.sons[0] = semExprBranchScope(c, x.sons[0])
+      typ = commonType(typ, x.sons[0])
+      hasElse = true
+    else:
+      illFormedAst(x, c.config)
+  if chckCovered:
+    if covered == toCover(c, n.sons[0].typ):
+      hasElse = true
+    else:
+      localError(c.config, n.info, "not all cases are covered")
+  closeScope(c)
+  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
+    for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
+    # propagate any enforced VoidContext:
+    if typ == c.enforceVoidContext:
+      result.typ = c.enforceVoidContext
+  else:
+    for i in 1..n.len-1:
+      var it = n.sons[i]
+      let j = it.len-1
+      if not endsInNoReturn(it.sons[j]):
+        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
+    result.typ = typ
+
 proc semRaise(c: PContext, n: PNode): PNode =
   result = n
   checkSonsLen(n, 1, c.config)
@@ -970,7 +1006,10 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
             var body = s.typ.lastSon
             if body.kind == tyObject:
               # erases all declared fields
-              body.n.sons = nil
+              when defined(nimNoNilSeqs):
+                body.n.sons = @[]
+              else:
+                body.n.sons = nil
 
       popOwner(c)
       closeScope(c)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 28dd15209..1669a7707 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -196,6 +196,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
         if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin {
               tyError, tyObject}:
           message c.config, n[i].info, errGenerated, "region needs to be an object type"
+        else:
+          message(c.config, n.info, warnDeprecated, "region for pointer types")
         addSonSkipIntLit(result, region)
     addSonSkipIntLit(result, t)
     if tfPartial in result.flags:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index ff7cb0bb0..c315cbebb 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -40,8 +40,8 @@ proc searchInstTypes*(key: PType): PType =
   if not (genericTyp.kind == tyGenericBody and
       key.sons[0] == genericTyp and genericTyp.sym != nil): return
 
-  if genericTyp.sym.typeInstCache == nil:
-    return
+  when not defined(nimNoNilSeqs):
+    if genericTyp.sym.typeInstCache == nil: return
 
   for inst in genericTyp.sym.typeInstCache:
     if inst.id == key.id: return inst
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 29f16b808..f206119ec 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -143,7 +143,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
       c.calleeScope = 1
   else:
     c.calleeScope = calleeScope
-  c.diagnostics = if diagnosticsEnabled: @[] else: nil
+  c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
   c.diagnosticsEnabled = diagnosticsEnabled
   c.magic = c.calleeSym.magic
   initIdTable(c.bindings)
@@ -535,6 +535,12 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
 proc allowsNil(f: PType): TTypeRelation {.inline.} =
   result = if tfNotNil notin f.flags: isSubtype else: isNone
 
+proc allowsNilDeprecated(c: TCandidate, f: PType): TTypeRelation =
+  if optNilSeqs in c.c.config.options:
+    result = allowsNil(f)
+  else:
+    result = isNone
+
 proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
   result = f.kind != a.kind and (f.kind in {tyVar, tyLent} or a.kind in {tyVar, tyLent})
 
@@ -741,7 +747,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     diagnostics = @[]
     flags = {efExplain}
     m.c.config.writelnHook = proc (s: string) =
-      if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
+      if errorPrefix.len == 0: errorPrefix = typeClass.sym.name.s & ":"
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
       diagnostics.add msg
@@ -858,6 +864,10 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
       if lhs[2].kind == nkIntLit:
         return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal)
 
+    of mAshrI:
+      if lhs[2].kind == nkIntLit:
+        return inferStaticParam(c, lhs[1], ashr(rhs, lhs[2].intVal))
+
     of mUnaryMinusI:
       return inferStaticParam(c, lhs[1], -rhs)
 
@@ -1249,7 +1259,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
             result = isNone
         elif tfNotNil in f.flags and tfNotNil notin a.flags:
           result = isNilConversion
-    of tyNil: result = f.allowsNil
+    of tyNil: result = allowsNilDeprecated(c, f)
     else: discard
   of tyOrdinal:
     if isOrdinalType(a):
@@ -1334,7 +1344,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
         result = isNilConversion
       else:
         result = isEqual
-    of tyNil: result = f.allowsNil
+    of tyNil: result = allowsNilDeprecated(c, f)
     else: discard
   of tyCString:
     # conversion from string to cstring is automatic:
@@ -1608,7 +1618,7 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
           if f.sonsLen == 0:
             result = isGeneric
           else:
-            internalAssert c.c.graph.config, a.sons != nil and a.sons.len > 0
+            internalAssert c.c.graph.config, a.len > 0
             c.typedescMatched = true
             var aa = a
             while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
@@ -1806,6 +1816,10 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
     let srca = typeRel(m, src, a)
     if srca notin {isEqual, isGeneric, isSubtype}: continue
 
+    let constraint = c.converters[i].typ.n[1].sym.constraint
+    if not constraint.isNil and not matchNodeKinds(constraint, arg):
+      continue
+
     let destIsGeneric = containsGenericType(dest)
     if destIsGeneric:
       dest = generateTypeInstance(c, m.bindings, arg, dest)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index acfccb5ff..84297aa6a 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -369,6 +369,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
       result = PTransNode(n.sons[0])
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         PNode(result).typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        PNode(result).typ = toVar(PNode(result).typ)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     var m = n.sons[0].sons[1]
     if m.kind == a or m.kind == b:
@@ -377,6 +379,8 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
       result = PTransNode(n.sons[0])
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
         PNode(result).typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        PNode(result).typ = toVar(PNode(result).typ)
   else:
     if n.sons[0].kind == a or n.sons[0].kind == b:
       # addr ( deref ( x )) --> x
@@ -539,11 +543,8 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
   if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
       call.sons[0].typ.callConv == ccClosure:
     n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
-    if not c.tooEarly:
-      n.sons[length-2] = transform(c, n.sons[length-2]).PNode
-      result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
-    else:
-      result[1] = newNode(nkEmpty).PTransNode
+    n.sons[length-2] = transform(c, n.sons[length-2]).PNode
+    result[1] = lambdalifting.liftForLoop(c.graph, n, getCurrOwner(c)).PTransNode
     discard c.breakSyms.pop
     return result
 
diff --git a/compiler/types.nim b/compiler/types.nim
index 514f5cee5..674819bc5 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -430,7 +430,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
     elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
       result = t.sym.name.s
-      if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
+      if t.kind == tyGenericParam and t.sonsLen > 0:
         result.add ": "
         var first = true
         for son in t.sons:
@@ -1202,7 +1202,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
      tyNone, tyForward, tyFromExpr:
     result = t
   of tyNil:
-    if kind != skConst: result = t
+    if kind != skConst and kind != skParam: result = t
   of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer:
     result = nil
   of tyOrdinal:
@@ -1539,10 +1539,9 @@ proc isCompileTimeOnly*(t: PType): bool {.inline.} =
 
 proc containsCompileTimeOnly*(t: PType): bool =
   if isCompileTimeOnly(t): return true
-  if t.sons != nil:
-    for i in 0 ..< t.sonsLen:
-      if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
-        return true
+  for i in 0 ..< t.sonsLen:
+    if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
+      return true
   return false
 
 type
diff --git a/compiler/vm.nim b/compiler/vm.nim
index a6ec4788b..c49b66b82 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -752,6 +752,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcShlInt:
       decodeBC(rkInt)
       regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
+    of opcAshrInt:
+      decodeBC(rkInt)
+      regs[ra].intVal = ashr(regs[rb].intVal, regs[rc].intVal)
     of opcBitandInt:
       decodeBC(rkInt)
       regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
@@ -1422,24 +1425,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       ensureKind(rkNode)
       if c.callsite != nil: regs[ra].node = c.callsite
       else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite")
-    of opcNGetFile:
-      decodeB(rkNode)
-      let n = regs[rb].node
-      regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
-      regs[ra].node.info = n.info
-      regs[ra].node.typ = n.typ
-    of opcNGetLine:
-      decodeB(rkNode)
+    of opcNGetLineInfo:
+      decodeBImm(rkNode)
       let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
+      case imm
+      of 0: # getFile
+        regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info))
+      of 1: # getLine
+        regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
+      of 2: # getColumn
+        regs[ra].node = newIntNode(nkIntLit, n.info.col)
+      else:
+        internalAssert c.config, false
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
-    of opcNGetColumn:
+    of opcNSetLineInfo:
       decodeB(rkNode)
-      let n = regs[rb].node
-      regs[ra].node = newIntNode(nkIntLit, n.info.col)
-      regs[ra].node.info = n.info
-      regs[ra].node.typ = n.typ
+      regs[ra].node.info = regs[rb].node.info
     of opcEqIdent:
       decodeBC(rkInt)
       # aliases for shorter and easier to understand code below
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 866b79568..1abd9ae4a 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -17,7 +17,7 @@ const
   byteExcess* = 128 # we use excess-K for immediates
   wordExcess* = 32768
 
-  MaxLoopIterations* = 1_000_000_000 # max iterations of all loops
+  MaxLoopIterations* = 3_000_000 # max iterations of all loops
 
 
 type
@@ -57,7 +57,8 @@ type
     opcLenStr,
 
     opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt,
-    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, opcShrInt, opcShlInt,
+    opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat,
+    opcShrInt, opcShlInt, opcAshrInt,
     opcBitandInt, opcBitorInt, opcBitxorInt, opcAddu, opcSubu, opcMulu,
     opcDivu, opcModu, opcEqInt, opcLeInt, opcLtInt, opcEqFloat,
     opcLeFloat, opcLtFloat, opcLeu, opcLtu,
@@ -102,7 +103,7 @@ type
     opcNError,
     opcNWarning,
     opcNHint,
-    opcNGetLine, opcNGetColumn, opcNGetFile,
+    opcNGetLineInfo, opcNSetLineInfo,
     opcEqIdent,
     opcStrToIdent,
     opcGetImpl,
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 3b5ea4beb..a36f559ca 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -939,6 +939,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     c.freeTemp(tmp2)
 
   of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
+  of mAshrI: genBinaryABCnarrow(c, n, dest, opcAshrInt)
   of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
   of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
   of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
@@ -1181,14 +1182,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType)
   of mNLineInfo:
     case n[0].sym.name.s
-    of "getFile":
-      genUnaryABC(c, n, dest, opcNGetFile)
-    of "getLine":
-      genUnaryABC(c, n, dest, opcNGetLine)
-    of "getColumn":
-      genUnaryABC(c, n, dest, opcNGetColumn)
-    else:
-      internalAssert c.config, false
+    of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0)
+    of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1)
+    of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2)
+    of "copyLineInfo":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfo)
+    else: internalAssert c.config, false
   of mNHint:
     unused(c, n, dest)
     genUnaryStmt(c, n, opcNHint)