summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorMichael Voronin <survivor.mail@gmail.com>2018-05-03 17:12:01 +0300
committerGitHub <noreply@github.com>2018-05-03 17:12:01 +0300
commit5ea967d97a30f0084883d4efa81b05bea3e5d148 (patch)
tree05ea0e3624f6720c2f5af28b5a70c87c85feafc7 /compiler
parent3949c9f977378ea3ab2b3c750f4dc2bc8d853022 (diff)
parent5564289b577c620cbd775f477b7fc8b6507adbfa (diff)
downloadNim-5ea967d97a30f0084883d4efa81b05bea3e5d148.tar.gz
Merge pull request #3 from nim-lang/devel
pull #3
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgcalls.nim8
-rw-r--r--compiler/ccgexprs.nim52
-rw-r--r--compiler/commands.nim17
-rw-r--r--compiler/condsyms.nim4
-rw-r--r--compiler/filter_tmpl.nim3
-rw-r--r--compiler/lexer.nim15
-rw-r--r--compiler/msgs.nim9
-rw-r--r--compiler/nimfix/nimfix.nim14
-rw-r--r--compiler/options.nim6
-rw-r--r--compiler/renderer.nim13
-rw-r--r--compiler/ropes.nim6
-rw-r--r--compiler/sem.nim5
-rw-r--r--compiler/semcall.nim27
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim10
-rw-r--r--compiler/semfold.nim96
-rw-r--r--compiler/semstmts.nim210
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/sigmatch.nim26
-rw-r--r--compiler/syntaxes.nim10
-rw-r--r--compiler/types.nim12
-rw-r--r--compiler/vmdeps.nim7
-rw-r--r--compiler/vmgen.nim1
23 files changed, 278 insertions, 277 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 5f14b6804..2ddc88509 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -125,15 +125,15 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
     of tyString, tySequence:
       if skipTypes(n.typ, abstractInst).kind == tyVar and
             not compileToCpp(p.module):
-        result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+        result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
       else:
-        result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
+        result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
     of tyArray:
       result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
     of tyPtr, tyRef:
       case lastSon(a.t).kind
       of tyString, tySequence:
-        result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
+        result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
       of tyArray:
         result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
       else:
@@ -143,7 +143,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
 proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  result = "$1->data" % [a.rdLoc]
+  result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc]
 
 proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
   var a: TLoc
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index ea373f5a6..96f9265f1 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -63,6 +63,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
     of tyNil:
       result = genNilStringLiteral(p.module, n.info)
     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)
     else:
       if n.strVal.isNil: result = rope("NIM_NIL")
@@ -882,7 +886,7 @@ proc genIndexCheck(p: BProc; arr, idx: TLoc) =
               rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
   of tySequence, tyString:
     linefmt(p, cpsStmts,
-          "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+          "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
           rdLoc(idx), rdLoc(arr), lenField(p))
   else: discard
 
@@ -905,14 +909,14 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
   if ty.kind in {tyRef, tyPtr}:
     ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
   if optBoundsCheck in p.options:
-    if ty.kind == tyString:
+    if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
       linefmt(p, cpsStmts,
-           "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
-           rdLoc(b), rdLoc(a), lenField(p))
+              "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
+              rdLoc(b), rdLoc(a), lenField(p))
     else:
       linefmt(p, cpsStmts,
-           "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
-           rdLoc(b), rdLoc(a), lenField(p))
+              "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
+              rdLoc(b), rdLoc(a), lenField(p))
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
     a.r = rfmt(nil, "(*$1)", a.r)
@@ -980,10 +984,10 @@ proc genEcho(p: BProc, n: PNode) =
     var a: TLoc
     for it in n.sons:
       if it.skipConv.kind == nkNilLit:
-        add(args, ", \"nil\"")
+        add(args, ", \"\"")
       else:
         initLocExpr(p, it, a)
-        addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
+        addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)])
     p.module.includeHeader("<base/log.h>")
     linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
   else:
@@ -1034,7 +1038,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
       if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
         inc(L, len(e.sons[i + 1].strVal))
       else:
-        addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
+        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
       add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
   linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
   add(p.s(cpsStmts), appends)
@@ -1073,7 +1077,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
       if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
         inc(L, len(e.sons[i + 2].strVal))
       else:
-        addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
+        addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)])
       add(appends, rfmt(p.module, "#appendString($1, $2);$n",
                         rdLoc(dest), rdLoc(a)))
   linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
@@ -1086,17 +1090,17 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   #    seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
   #    seq->data[seq->len-1] = x;
   let seqAppendPattern = if not p.module.compileToCpp:
-                           "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
+                           "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
                          else:
-                           "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
+                           "$1 = ($2) #incrSeqV3($1, $3);$n"
   var a, b, dest, tmpL: TLoc
   initLocExpr(p, e.sons[1], a)
   initLocExpr(p, e.sons[2], b)
-  let bt = skipTypes(e.sons[2].typ, {tyVar})
+  let seqType = skipTypes(e.sons[1].typ, {tyVar})
   lineCg(p, cpsStmts, seqAppendPattern, [
       rdLoc(a),
       getTypeDesc(p.module, e.sons[1].typ),
-      getTypeDesc(p.module, bt)])
+      genTypeInfo(p.module, seqType, e.info)])
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
   initLoc(dest, locExpr, e.sons[2], OnHeap)
@@ -1417,7 +1421,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
       putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
       putIntoDest(p, b, e,
-                  "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage)
+                  "$1->data, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)], a.storage)
     of tyArray:
       putIntoDest(p, b, e,
                   "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage)
@@ -1500,13 +1504,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   initLocExpr(p, e.sons[2], b)
   let t = skipTypes(e.sons[1].typ, {tyVar})
   let setLenPattern = if not p.module.compileToCpp:
-      "$1 = ($3) #setLengthSeq(&($1)->Sup, sizeof($4), $2);$n"
+      "$1 = ($3) #setLengthSeqV2(&($1)->Sup, $4, $2);$n"
     else:
-      "$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n"
+      "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
 
   lineCg(p, cpsStmts, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
-      getTypeDesc(p.module, t.skipTypes(abstractInst).sons[0])])
+      genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)])
   gcUsage(e)
 
 proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
@@ -1740,7 +1744,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
 proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
   initLocExpr(p, n.sons[0], a)
-  putIntoDest(p, d, n, "$1->data" % [rdLoc(a)],
+  putIntoDest(p, d, n, "($1 ? $1->data : (NCSTRING)\"\")" % [rdLoc(a)],
               a.storage)
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
@@ -1755,16 +1759,14 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
   var x: TLoc
   var a = e.sons[1]
   var b = e.sons[2]
-  if (a.kind == nkNilLit) or (b.kind == nkNilLit):
-    binaryExpr(p, e, d, "($1 == $2)")
-  elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
+  if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
     initLocExpr(p, e.sons[2], x)
     putIntoDest(p, d, e,
-      rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
-  elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""):
+      rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+  elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
     initLocExpr(p, e.sons[1], x)
     putIntoDest(p, d, e,
-      rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p)))
+      rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
   else:
     binaryExpr(p, e, d, "#eqStrings($1, $2)")
 
diff --git a/compiler/commands.nim b/compiler/commands.nim
index 06b487cf0..d8d8ae4b7 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -54,6 +54,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
 
 const
   HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
+      "Compiled at $4 $5\n" &
       "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
 
 const
@@ -68,7 +69,8 @@ const
 
 proc getCommandLineDesc(): string =
   result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
-                           CPU[platform.hostCPU].name]) & Usage
+                           CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
+                           Usage
 
 proc helpOnError(pass: TCmdLinePass) =
   if pass == passCmd1:
@@ -79,7 +81,8 @@ proc writeAdvancedUsage(pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(`%`(HelpMessage, [VersionAsString,
                                  platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name]) & AdvancedUsage,
+                                 CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
+                                 AdvancedUsage,
                {msgStdout})
     msgQuit(0)
 
@@ -87,7 +90,8 @@ proc writeFullhelp(pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(`%`(HelpMessage, [VersionAsString,
                                  platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name]) & Usage & AdvancedUsage,
+                                 CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
+                                 Usage & AdvancedUsage,
                {msgStdout})
     msgQuit(0)
 
@@ -95,7 +99,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
   if pass == passCmd1:
     msgWriteln(`%`(HelpMessage, [VersionAsString,
                                  platform.OS[platform.hostOS].name,
-                                 CPU[platform.hostCPU].name]),
+                                 CPU[platform.hostCPU].name, CompileDate, CompileTime]),
                {msgStdout})
 
     const gitHash = gorge("git log -n 1 --format=%H").strip
@@ -188,11 +192,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
   if i < len(arg) and (arg[i] in {':', '='}): inc(i)
   else: invalidCmdLineOption(pass, orig, info)
   if state == wHint:
-    var x = findStr(msgs.HintsToStr, id)
+    let x = findStr(msgs.HintsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(hintMin))
     else: localError(info, "unknown hint: " & id)
   else:
-    var x = findStr(msgs.WarningsToStr, id)
+    let x = findStr(msgs.WarningsToStr, id)
     if x >= 0: n = TNoteKind(x + ord(warnMin))
     else: localError(info, "unknown warning: " & id)
   case substr(arg, i).normalize
@@ -499,6 +503,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       undefSymbol("nimOldNewlines")
     else:
       localError(info, errOnOrOffExpectedButXFound, arg)
+  of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info)
   of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
   of "floatchecks":
     processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 028aedb5b..f8a75e68e 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -82,7 +82,6 @@ proc countDefinedSymbols*(): int =
 
 proc initDefines*() =
   gSymbols = newStringTable(modeStyleInsensitive)
-  defineSymbol("nimrod") # 'nimrod' is always defined
   # for bootstrapping purposes and old code:
   defineSymbol("nimhygiene")
   defineSymbol("niminheritable")
@@ -115,3 +114,6 @@ proc initDefines*() =
   defineSymbol("nimHasNilChecks")
   defineSymbol("nimSymKind")
   defineSymbol("nimVmEqIdent")
+  defineSymbol("nimNoNil")
+  defineSymbol("nimNoZeroTerminator")
+  defineSymbol("nimNotNil")
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index a1ba9113c..51ccb8390 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -42,7 +42,8 @@ proc newLine(p: var TTmplParser) =
 
 proc scanPar(p: var TTmplParser, d: int) =
   var i = d
-  while true:
+  let hi = p.x.len - 1
+  while i <= hi:
     case p.x[i]
     of '\0': break
     of '(': inc(p.par)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 0b1090bb1..0478ed574 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -165,13 +165,12 @@ proc isKeyword*(kind: TTokType): bool =
 template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
 
 proc isNimIdentifier*(s: string): bool =
-  if s[0] in SymStartChars:
+  let sLen = s.len
+  if sLen > 0 and s[0] in SymStartChars:
     var i = 1
-    var sLen = s.len
     while i < sLen:
-      if s[i] == '_':
-        inc(i)
-      if s[i] notin SymChars: return
+      if s[i] == '_': inc(i)
+      if i < sLen and s[i] notin SymChars: return
       inc(i)
     result = true
 
@@ -311,12 +310,12 @@ template tokenEndPrevious(tok, pos) =
 # We need to parse the largest uint literal without overflow checks
 proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
   var i = start
-  if s[i] in {'0'..'9'}:
+  if i < s.len and s[i] in {'0'..'9'}:
     b = 0
-    while s[i] in {'0'..'9'}:
+    while i < s.len and s[i] in {'0'..'9'}:
       b = b * 10 + (ord(s[i]) - ord('0'))
       inc(i)
-      while s[i] == '_': inc(i) # underscores are allowed and ignored
+      while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     result = i - start
 {.pop.} # overflowChecks
 
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 5ae2c4970..5da375c1c 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -126,7 +126,7 @@ type
     warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
     warnUnknownSubstitutionX, warnLanguageXNotSupported,
     warnFieldXNotSupported, warnCommentXIgnored,
-    warnNilStatement, warnTypelessParam,
+    warnTypelessParam,
     warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
     warnEachIdentIsTuple, warnShadowIdent,
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
@@ -400,7 +400,6 @@ const
     warnLanguageXNotSupported: "language \'$1\' not supported",
     warnFieldXNotSupported: "field \'$1\' not supported",
     warnCommentXIgnored: "comment \'$1\' ignored",
-    warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead",
     warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
     warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
     warnWriteToForeignHeap: "write to foreign heap",
@@ -451,7 +450,7 @@ const
     "SmallLshouldNotBeUsed", "UnknownMagic",
     "RedefinitionOfLabel", "UnknownSubstitutionX",
     "LanguageXNotSupported", "FieldXNotSupported",
-    "CommentXIgnored", "NilStmt",
+    "CommentXIgnored",
     "TypelessParam", "UseBase", "WriteToForeignHeap",
     "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
     "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
@@ -474,6 +473,10 @@ const
   hintMin* = hintSuccess
   hintMax* = high(TMsgKind)
 
+static:
+  doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
+  doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
+
 type
   TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
   TNoteKinds* = set[TNoteKind]
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
index a97d88078..2ef375b00 100644
--- a/compiler/nimfix/nimfix.nim
+++ b/compiler/nimfix/nimfix.nim
@@ -47,7 +47,7 @@ proc mainCommand =
   compileProject(newModuleGraph(), newIdentCache())
   pretty.overwriteFiles()
 
-proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
+proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
   var argsCount = 0
   gOnlyMainfile = true
@@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
       of "wholeproject": gOnlyMainfile = false
       of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
       else:
-        processSwitch(pass, p)
+        processSwitch(pass, p, config)
     of cmdArgument:
       options.gProjectName = unixToNativePath(p.key)
       # if processArgument(pass, p, argsCount): break
 
-proc handleCmdLine() =
+proc handleCmdLine(config: ConfigRef) =
   if paramCount() == 0:
     stdout.writeLine(Usage)
   else:
-    processCmdLine(passCmd1, "")
+    processCmdLine(passCmd1, "", config)
     if gProjectName != "":
       try:
         gProjectFull = canonicalizePath(gProjectName)
@@ -96,11 +96,11 @@ proc handleCmdLine() =
       gProjectName = p.name
     else:
       gProjectPath = getCurrentDir()
-    loadConfigs(DefaultConfig) # load all config files
+    loadConfigs(DefaultConfig, config) # load all config files
     # now process command line arguments again, because some options in the
     # command line can overwite the config file's settings
     extccomp.initVars()
-    processCmdLine(passCmd2, "")
+    processCmdLine(passCmd2, "", config)
     mainCommand()
 
 when compileOption("gc", "v2") or compileOption("gc", "refc"):
@@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
 
 condsyms.initDefines()
 defineSymbol "nimfix"
-handleCmdline()
+handleCmdline newConfigRef()
diff --git a/compiler/options.nim b/compiler/options.nim
index a5dfaa81c..5baaa1bfd 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -37,7 +37,8 @@ type                          # please make sure we have under 32 options
                               # evaluation
     optPatterns,              # en/disable pattern matching
     optMemTracker,
-    optHotCodeReloading
+    optHotCodeReloading,
+    optLaxStrings
 
   TOptions* = set[TOption]
   TGlobalOption* = enum       # **keep binary compatible**
@@ -108,7 +109,8 @@ type
     dotOperators,
     callOperator,
     parallel,
-    destructor
+    destructor,
+    notnil
 
   ConfigRef* = ref object ## eventually all global configuration should be moved here
     cppDefines*: HashSet[string]
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 2a65a10a8..95a622d4e 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -175,10 +175,11 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
 proc putComment(g: var TSrcGen, s: string) =
   if s.isNil: return
   var i = 0
+  let hi = len(s) - 1
   var isCode = (len(s) >= 2) and (s[1] != ' ')
   var ind = g.lineLen
   var com = "## "
-  while true:
+  while i <= hi:
     case s[i]
     of '\0':
       break
@@ -201,12 +202,12 @@ proc putComment(g: var TSrcGen, s: string) =
       # gets too long:
       # compute length of the following word:
       var j = i
-      while s[j] > ' ': inc(j)
+      while j <= hi and s[j] > ' ': inc(j)
       if not isCode and (g.lineLen + (j - i) > MaxLineLen):
         put(g, tkComment, com)
         optNL(g, ind)
         com = "## "
-      while s[i] > ' ':
+      while i <= hi and s[i] > ' ':
         add(com, s[i])
         inc(i)
   put(g, tkComment, com)
@@ -215,8 +216,9 @@ proc putComment(g: var TSrcGen, s: string) =
 proc maxLineLength(s: string): int =
   if s.isNil: return 0
   var i = 0
+  let hi = len(s) - 1
   var lineLen = 0
-  while true:
+  while i <= hi:
     case s[i]
     of '\0':
       break
@@ -235,7 +237,7 @@ proc maxLineLength(s: string): int =
 
 proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
   var i = 0
-  var hi = len(s) - 1
+  let hi = len(s) - 1
   var str = ""
   while i <= hi:
     case s[i]
@@ -1064,6 +1066,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
     if n.len > 1:
       let opr = if n[0].kind == nkIdent: n[0].ident
                 elif n[0].kind == nkSym: n[0].sym.name
+                elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
                 else: nil
       if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
         put(g, tkSpaces, Space)
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 358ce8a53..05d5e840c 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string =
     var stack = @[r]
     while stack.len > 0:
       var it = stack.pop
-      while isNil(it.data):
+      while it.left != nil:
+        assert it.right != nil
         stack.add(it.right)
         it = it.left
         assert(it != nil)
-      assert(it.data != nil)
       yield it.data
 
 iterator items*(r: Rope): char =
@@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
         while true:
           j = j * 10 + ord(frmt[i]) - ord('0')
           inc(i)
-          if frmt[i] notin {'0'..'9'}: break
+          if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         num = j
         if j > high(args) + 1:
           errorHandler(rInvalidFormatStr, $(j))
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 041f2e127..52282d0e4 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -153,8 +153,9 @@ proc commonType*(x, y: PType): PType =
     if a.kind in {tyRef, tyPtr}:
       k = a.kind
       if b.kind != a.kind: return x
-      a = a.lastSon
-      b = b.lastSon
+      # bug #7601, array construction of ptr generic
+      a = a.lastSon.skipTypes({tyGenericInst})
+      b = b.lastSon.skipTypes({tyGenericInst})
     if a.kind == tyObject and b.kind == tyObject:
       result = commonSuperclass(a, b)
       # this will trigger an error later:
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index f443339f5..aa53fda3b 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        filter: TSymKinds,
                        best, alt: var TCandidate,
                        errors: var CandidateErrors,
-                       diagnosticsFlag = false) =
+                       diagnosticsFlag: bool,
+                       errorsEnabled: bool) =
   var o: TOverloadIter
   var sym = initOverloadIter(o, c, headSymbol)
   var scope = o.lastOverloadScope
@@ -68,6 +69,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
   # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
   let counterInitial = c.currentScope.symbols.counter
   var syms: seq[tuple[s: PSym, scope: int]]
+  var noSyms = true
   var nextSymIndex = 0
   while sym != nil:
     if sym.kind in filter:
@@ -102,7 +104,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
           var cmp = cmpCandidates(best, z)
           if cmp < 0: best = z   # x is better than the best so far
           elif cmp == 0: alt = z # x is as good as the best so far
-      elif errors != nil or z.diagnostics != nil:
+      elif errorsEnabled or z.diagnosticsEnabled:
         errors.safeAdd(CandidateError(
           sym: sym,
           unmatchedVarParam: int z.mutabilityProblem,
@@ -113,7 +115,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
       # before any further candidate init and compare. SLOW, but rare case.
       syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
                                   best, alt, o, diagnosticsFlag)
-    if syms == nil:
+      noSyms = false
+    if noSyms:
       sym = nextOverloadIter(o, c, headSymbol)
       scope = o.lastOverloadScope
     elif nextSymIndex < syms.len:
@@ -206,7 +209,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   if errorOutputs == {}:
     # fail fast:
     globalError(n.info, errTypeMismatch, "")
-  if errors.isNil or errors.len == 0:
+  if errors.len == 0:
     localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
     return
 
@@ -227,7 +230,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
     if symx.kind in routineKinds:
       errors.add(CandidateError(sym: symx,
                                 unmatchedVarParam: 0, firstMismatch: 0,
-                                diagnostics: nil))
+                                diagnostics: nil,
+                                enabled: false))
     symx = nextOverloadIter(o, c, headSymbol)
   if errors.len == 0:
     localError(n.info, "could not resolve: " & $n)
@@ -236,7 +240,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
 
 proc resolveOverloads(c: PContext, n, orig: PNode,
                       filter: TSymKinds, flags: TExprFlags,
-                      errors: var CandidateErrors): TCandidate =
+                      errors: var CandidateErrors,
+                      errorsEnabled: bool): TCandidate =
   var initialBinding: PNode
   var alt: TCandidate
   var f = n.sons[0]
@@ -249,7 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
   template pickBest(headSymbol) =
     pickBestCandidate(c, headSymbol, n, orig, initialBinding,
-                      filter, result, alt, errors, efExplain in flags)
+                      filter, result, alt, errors, efExplain in flags,
+                      errorsEnabled)
   pickBest(f)
 
   let overloadsState = result.state
@@ -423,9 +429,8 @@ 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 r = resolveOverloads(c, n, nOrig, filter, flags, errors)
+  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
     if errors.len > 0:
@@ -443,7 +448,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
     # into sigmatch with hidden conversion produced there
     #
     n.sons[1] = n.sons[1].tryDeref
-    var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
+    var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
     if r.state == csMatch: result = semResolvedCall(c, n, r)
     else:
       # get rid of the deref again for a better error message:
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 8159abf8f..fc0488814 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -63,7 +63,7 @@ type
       # to the user.
     efWantStmt, efAllowStmt, efDetermineType, efExplain,
     efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
-    efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
+    efNoEvaluateGeneric, efInCall, efFromHlo
 
   TExprFlags* = set[TExprFlag]
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 6ad5d931d..feca087fc 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -51,7 +51,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
                 renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
   else:
-    if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
 proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
@@ -63,8 +62,6 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     localError(n.info, errExprXHasNoType,
                renderTree(result, {renderNoComments}))
     result.typ = errorType(c)
-  else:
-    semProcvarCheck(c, result)
 
 proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   result = symChoice(c, n, s, scClosed)
@@ -308,7 +305,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
     maybeLiftType(t2, c, n.info)
     var m: TCandidate
     initCandidate(c, m, t2)
-    if efExplain in flags: m.diagnostics = @[]
+    if efExplain in flags:
+      m.diagnostics = @[]
+      m.diagnosticsEnabled = true
     let match = typeRel(m, t2, t1) >= isSubtype # isNone
     result = newIntNode(nkIntLit, ord(match))
 
@@ -540,7 +539,6 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
       # we need to recurse explicitly here as converters can create nested
       # calls and then they wouldn't be analysed otherwise
       analyseIfAddressTakenInCall(c, n.sons[i])
-    semProcvarCheck(c, n.sons[i])
     if i < sonsLen(t) and
         skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
       if n.sons[i].kind != nkHiddenAddr:
@@ -1245,7 +1243,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   checkMinSonsLen(n, 2)
   # make sure we don't evaluate generic macros/templates
   n.sons[0] = semExprWithType(c, n.sons[0],
-                              {efNoProcvarCheck, efNoEvaluateGeneric})
+                              {efNoEvaluateGeneric})
   let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
                                       tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
   case arr.kind
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 6fcc9a0a4..c4d79a4a3 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -13,7 +13,7 @@
 import
   strutils, options, ast, astalgo, trees, treetab, nimsets, times,
   nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
-  commands, magicsys, saturate
+  commands, magicsys
 
 proc getConstExpr*(m: PSym, n: PNode): PNode
   # evaluates the constant expression or returns nil if it is no constant
@@ -24,6 +24,63 @@ proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
 proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
 proc newStrNodeT*(strVal: string, n: PNode): PNode
 
+proc checkInRange(n: PNode, res: BiggestInt): bool =
+  if res in firstOrd(n.typ)..lastOrd(n.typ):
+    result = true
+
+proc foldAdd(a, b: BiggestInt, n: PNode): PNode =
+  let res = a +% b
+  if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
+      checkInRange(n, res):
+    result = newIntNodeT(res, n)     
+
+proc foldSub*(a, b: BiggestInt, n: PNode): PNode =
+  let res = a -% b
+  if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
+      checkInRange(n, res):
+    result = newIntNodeT(res, n)
+
+proc foldAbs*(a: BiggestInt, n: PNode): PNode =
+  if a != firstOrd(n.typ):
+    result = newIntNodeT(a, n)
+  
+proc foldMod*(a, b: BiggestInt, n: PNode): PNode =
+  if b != 0'i64:
+    result = newIntNodeT(a mod b, n)
+
+proc foldModU*(a, b: BiggestInt, n: PNode): PNode =
+  if b != 0'i64:
+    result = newIntNodeT(a %% b, n)
+
+proc foldDiv*(a, b: BiggestInt, n: PNode): PNode =
+  if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
+    result = newIntNodeT(a div b, n)
+
+proc foldDivU*(a, b: BiggestInt, n: PNode): PNode =
+  if b != 0'i64:
+    result = newIntNodeT(a /% b, n)
+
+proc foldMul*(a, b: BiggestInt, n: PNode): PNode =
+  let res = a *% b
+  let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
+  let resAsFloat = toBiggestFloat(res)
+
+  # Fast path for normal case: small multiplicands, and no info
+  # is lost in either method.
+  if resAsFloat == floatProd and checkInRange(n, res):
+    return newIntNodeT(res, n)
+
+  # Somebody somewhere lost info. Close enough, or way off? Note
+  # that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
+  # The difference either is or isn't significant compared to the
+  # true value (of which floatProd is a good approximation).
+
+  # abs(diff)/abs(prod) <= 1/32 iff
+  #   32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
+  if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
+      checkInRange(n, res):
+    return newIntNodeT(res, n)
+
 # implementation
 
 proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
@@ -172,23 +229,22 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
   of mToFloat, mToBiggestFloat:
     result = newFloatNodeT(toFloat(int(getInt(a))), n)
+  # XXX: Hides overflow/underflow
   of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
   of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
-  of mAbsI:
-    if getInt(a) >= 0: result = a
-    else: result = newIntNodeT(- getInt(a), n)
+  of mAbsI: result = foldAbs(getInt(a), n)
   of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
     # byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
     result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
   of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
   of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
   of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
-  of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
-  of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
-  of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
-  of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
-  of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
-  of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
+  of mUnaryLt: result = foldSub(getOrdValue(a), 1, n)
+  of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n)
+  of mPred: result = foldSub(getOrdValue(a), getInt(b), n)
+  of mAddI: result = foldAdd(getInt(a), getInt(b), n)
+  of mSubI: result = foldSub(getInt(a), getInt(b), n)
+  of mMulI: result = foldMul(getInt(a), getInt(b), n)
   of mMinI:
     if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
     else: result = newIntNodeT(getInt(a), n)
@@ -211,14 +267,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
     of tyInt64, tyInt, tyUInt..tyUInt64:
       result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
     else: internalError(n.info, "constant folding for shr")
-  of mDivI:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`|div|`(getInt(a), y), n)
-  of mModI:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`|mod|`(getInt(a), y), n)
+  of mDivI: result = foldDiv(getInt(a), getInt(b), n)
+  of mModI: result = foldMod(getInt(a), getInt(b), n)
   of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
   of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
   of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
@@ -258,14 +308,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
   of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
   of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
   of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
-  of mModU:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`%%`(getInt(a), y), n)
-  of mDivU:
-    let y = getInt(b)
-    if y != 0:
-      result = newIntNodeT(`/%`(getInt(a), y), n)
+  of mModU: result = foldModU(getInt(a), getInt(b), n)
+  of mDivU: result = foldDivU(getInt(a), getInt(b), n)
   of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
   of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
   of mLtSet:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 94090852f..8b466f1da 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -71,37 +71,12 @@ proc toCover(t: PType): BiggestInt =
   else:
     result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
 
-when false:
-  proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) =
-    ## Checks that the given symbol is a proper procedure variable, meaning
-    ## that it
-    var smoduleId = getModule(s).id
-    if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
-        smoduleId != c.module.id:
-      block outer:
-        for module in c.friendModules:
-          if smoduleId == module.id:
-            break outer
-        localError(info, errXCannotBePassedToProcVar, s.name.s)
-
-template semProcvarCheck(c: PContext, n: PNode) =
-  when false:
-    var n = n.skipConv
-    if n.kind in nkSymChoices:
-      for x in n:
-        if x.sym.kind in {skProc, skMethod, skConverter, skIterator}:
-          performProcvarCheck(c, n.info, x.sym)
-    elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
-                                          skIterator}:
-      performProcvarCheck(c, n.info, n.sym)
-
 proc semProc(c: PContext, n: PNode): PNode
 
 proc semExprBranch(c: PContext, n: PNode): PNode =
   result = semExpr(c, n)
   if result.typ != nil:
     # XXX tyGenericInst here?
-    semProcvarCheck(c, result)
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
 proc semExprBranchScope(c: PContext, n: PNode): PNode =
@@ -132,30 +107,23 @@ proc fixNilType(n: PNode) =
 proc discardCheck(c: PContext, result: PNode) =
   if c.matchedConcept != nil: return
   if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
-    if result.kind == nkNilLit:
-      result.typ = nil
-      message(result.info, warnNilStatement)
-    elif implicitlyDiscardable(result):
+    if implicitlyDiscardable(result):
       var n = result
       result.typ = nil
       while n.kind in skipForDiscardable:
         n = n.lastSon
         n.typ = nil
     elif result.typ.kind != tyError and gCmd != cmdInteractive:
-      if result.typ.kind == tyNil:
-        fixNilType(result)
-        message(result.info, warnNilStatement)
-      else:
-        var n = result
-        while n.kind in skipForDiscardable: n = n.lastSon
-        var s = "expression '" & $n & "' is of type '" &
-            result.typ.typeToString & "' and has to be discarded"
-        if result.info.line != n.info.line or
-           result.info.fileIndex != n.info.fileIndex:
-          s.add "; start of expression here: " & $result.info
-        if result.typ.kind == tyProc:
-          s.add "; for a function call use ()"
-        localError(n.info, s)
+      var n = result
+      while n.kind in skipForDiscardable: n = n.lastSon
+      var s = "expression '" & $n & "' is of type '" &
+          result.typ.typeToString & "' and has to be discarded"
+      if result.info.line != n.info.line or
+          result.info.fileIndex != n.info.fileIndex:
+        s.add "; start of expression here: " & $result.info
+      if result.typ.kind == tyProc:
+        s.add "; for a function call use ()"
+      localError(n.info, s)
 
 proc semIf(c: PContext, n: PNode): PNode =
   result = n
@@ -384,7 +352,7 @@ proc checkNilable(v: PSym) =
 
 include semasgn
 
-proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
+proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
   let L = identDefs.len
   let value = identDefs[L-1]
   if result.kind == nkStmtList:
@@ -525,12 +493,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         localError(a.info, errXExpected, "tuple")
       elif length-2 != sonsLen(tup):
         localError(a.info, errWrongNumberOfVariables)
-      else:
-        b = newNodeI(nkVarTuple, a.info)
-        newSons(b, length)
-        b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
-        b.sons[length-1] = def
-        addToVarSection(c, result, n, b)
+      b = newNodeI(nkVarTuple, a.info)
+      newSons(b, length)
+      b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
+      b.sons[length-1] = def
+      addToVarSection(c, result, n, b)
     elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
         a.kind == nkIdentDefs and a.len > 3:
       message(a.info, warnEachIdentIsTuple)
@@ -572,7 +539,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
         addToVarSection(c, result, n, b)
       else:
         if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
-        setVarType(v, tup.sons[j])
+        # bug #7663, for 'nim check' this can be a non-tuple:
+        if tup.kind == tyTuple: setVarType(v, tup.sons[j])
+        else: v.typ = tup
         b.sons[j] = newSymNode(v)
       checkNilable(v)
       if sfCompileTime in v.flags: hasCompileTime = true
@@ -1588,9 +1557,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
   if s.name.s[0] in {'.', '('}:
     if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
-      message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
+      localError(n.info, "the overloaded " & s.name.s &
+        " operator has to be enabled with {.experimental: \"dotOperators\".}")
     elif s.name.s == "()" and callOperator notin c.features:
-      message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s)
+      localError(n.info, "the overloaded " & s.name.s &
+        " operator has to be enabled with {.experimental: \"callOperator\".}")
 
   if n.sons[bodyPos].kind != nkEmpty:
     # for DLL generation it is annoying to check for sfImportc!
@@ -1682,11 +1653,6 @@ proc semIterator(c: PContext, n: PNode): PNode =
     incl(s.typ.flags, tfCapturesEnv)
   else:
     s.typ.callConv = ccInline
-  when false:
-    if s.typ.callConv != ccInline:
-      s.typ.callConv = ccClosure
-      # and they always at least use the 'env' for the state field:
-      incl(s.typ.flags, tfCapturesEnv)
   if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
     localError(n.info, errImplOfXexpected, s.name.s)
 
@@ -1792,14 +1758,6 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
   evalStaticStmt(c.module, c.cache, a, c.p.owner)
   result = newNodeI(nkDiscardStmt, n.info, 1)
   result.sons[0] = emptyNode
-  when false:
-    result = evalStaticStmt(c.module, a, c.p.owner)
-    if result.isNil:
-      LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
-      result = emptyNode
-    elif result.kind == nkEmpty:
-      result = newNodeI(nkDiscardStmt, n.info, 1)
-      result.sons[0] = emptyNode
 
 proc usesResult(n: PNode): bool =
   # nkStmtList(expr) properly propagates the void context,
@@ -1841,80 +1799,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
   #                                         nkNilLit, nkEmpty}:
   #  dec last
   for i in countup(0, length - 1):
-    let k = n.sons[i].kind
-    case k
-    of nkFinally, nkExceptBranch:
-      # stand-alone finally and except blocks are
-      # transformed into regular try blocks:
-      #
-      # var f = fopen("somefile") | var f = fopen("somefile")
-      # finally: fclose(f)        | try:
-      # ...                       |   ...
-      #                           | finally:
-      #                           |   fclose(f)
-      var deferPart: PNode
-      if k == nkDefer:
-        deferPart = newNodeI(nkFinally, n.sons[i].info)
-        deferPart.add n.sons[i].sons[0]
-      elif k == nkFinally:
-        message(n.info, warnDeprecated,
-                "use 'defer'; standalone 'finally'")
-        deferPart = n.sons[i]
-      else:
-        message(n.info, warnDeprecated,
-                "use an explicit 'try'; standalone 'except'")
-        deferPart = n.sons[i]
-      var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
-      var body = newNodeI(nkStmtList, n.sons[i].info)
-      if i < n.sonsLen - 1:
-        body.sons = n.sons[(i+1)..n.len-1]
-      tryStmt.addSon(body)
-      tryStmt.addSon(deferPart)
-      n.sons[i] = semTry(c, tryStmt)
-      n.sons.setLen(i+1)
+    var expr = semExpr(c, n.sons[i], flags)
+    n.sons[i] = expr
+    if c.matchedConcept != nil and expr.typ != nil and
+        (nfFromTemplate notin n.flags or i != last):
+      case expr.typ.kind
+      of tyBool:
+        if expr.kind == nkInfix and
+            expr[0].kind == nkSym and
+            expr[0].sym.name.s == "==":
+          if expr[1].typ.isUnresolvedStatic:
+            inferConceptStaticParam(c, expr[1], expr[2])
+            continue
+          elif expr[2].typ.isUnresolvedStatic:
+            inferConceptStaticParam(c, expr[2], expr[1])
+            continue
+
+        let verdict = semConstExpr(c, n[i])
+        if verdict.intVal == 0:
+          localError(result.info, "concept predicate failed")
+      of tyUnknown: continue
+      else: discard
+    if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
+      voidContext = true
+      n.typ = enforceVoidContext
+    if i == last and (length == 1 or efWantValue in flags):
       n.typ = n.sons[i].typ
-      return
+      if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+    elif i != last or voidContext:
+      discardCheck(c, n.sons[i])
     else:
-      var expr = semExpr(c, n.sons[i], flags)
-      n.sons[i] = expr
-      if c.matchedConcept != nil and expr.typ != nil and
-         (nfFromTemplate notin n.flags or i != last):
-        case expr.typ.kind
-        of tyBool:
-          if expr.kind == nkInfix and
-             expr[0].kind == nkSym and
-             expr[0].sym.name.s == "==":
-            if expr[1].typ.isUnresolvedStatic:
-              inferConceptStaticParam(c, expr[1], expr[2])
-              continue
-            elif expr[2].typ.isUnresolvedStatic:
-              inferConceptStaticParam(c, expr[2], expr[1])
-              continue
-
-          let verdict = semConstExpr(c, n[i])
-          if verdict.intVal == 0:
-            localError(result.info, "concept predicate failed")
-        of tyUnknown: continue
-        else: discard
-      if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
-        voidContext = true
-        n.typ = enforceVoidContext
-      if i == last and (length == 1 or efWantValue in flags):
-        n.typ = n.sons[i].typ
-        if not isEmptyType(n.typ): n.kind = nkStmtListExpr
-      elif i != last or voidContext:
-        discardCheck(c, n.sons[i])
-      else:
-        n.typ = n.sons[i].typ
-        if not isEmptyType(n.typ): n.kind = nkStmtListExpr
-      if n.sons[i].kind in LastBlockStmts or
-         n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
-        for j in countup(i + 1, length - 1):
-          case n.sons[j].kind
-          of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
-             nkBlockStmt, nkState: discard
-          else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
-      else: discard
+      n.typ = n.sons[i].typ
+      if not isEmptyType(n.typ): n.kind = nkStmtListExpr
+    if n.sons[i].kind in LastBlockStmts or
+        n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and
+        sfNoReturn in n.sons[i][0].sym.flags:
+      for j in countup(i + 1, length - 1):
+        case n.sons[j].kind
+        of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
+            nkBlockStmt, nkState: discard
+        else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
+    else: discard
 
   if result.len == 1 and
      # concept bodies should be preserved as a stmt list:
@@ -1930,15 +1855,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
         not (result.comment[0] == '#' and result.comment[1] == '#'):
       # it is an old-style comment statement: we replace it with 'discard ""':
       prettybase.replaceComment(result.info)
-  when false:
-    # a statement list (s; e) has the type 'e':
-    if result.kind == nkStmtList and result.len > 0:
-      var lastStmt = lastSon(result)
-      if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt):
-        result.typ = lastStmt.typ
-        #localError(lastStmt.info, errGenerated,
-        #  "Last expression must be explicitly returned if it " &
-        #  "is discardable or discarded")
 
 proc semStmt(c: PContext, n: PNode): PNode =
   # now: simply an alias:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 1fc263617..537319d66 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1389,6 +1389,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
               n.sons[2].kind == nkNilLit:
             result = freshType(result, prev)
             result.flags.incl(tfNotNil)
+            if notnil notin c.features:
+              localError(n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}")
           else:
             localError(n.info, errGenerated, "invalid type")
         of 2:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 96d815df7..4263ef581 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -26,6 +26,7 @@ type
     sym*: PSym
     unmatchedVarParam*, firstMismatch*: int
     diagnostics*: seq[string]
+    enabled*: bool
 
   CandidateErrors* = seq[CandidateError]
 
@@ -60,7 +61,8 @@ type
                               # matching. they will be reset if the matching
                               # is not successful. may replace the bindings
                               # table in the future.
-    diagnostics*: seq[string] # when this is not nil, the matching process
+    diagnostics*: seq[string] # \
+                              # when diagnosticsEnabled, the matching process
                               # will collect extra diagnostics that will be
                               # displayed to the user.
                               # triggered when overload resolution fails
@@ -70,6 +72,7 @@ type
     inheritancePenalty: int   # to prefer closest father object type
     firstMismatch*: int       # position of the first type mismatch for
                               # better error messages
+    diagnosticsEnabled*: bool
 
   TTypeRelFlag* = enum
     trDontBind
@@ -124,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
   idTablePut(c.bindings, key, val.skipIntLit)
 
 proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
-                    binding: PNode, calleeScope = -1, diagnostics = false) =
+                    binding: PNode, calleeScope = -1,
+                    diagnosticsEnabled = false) =
   initCandidateAux(ctx, c, callee.typ)
   c.calleeSym = callee
   if callee.kind in skProcKinds and calleeScope == -1:
@@ -139,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
       c.calleeScope = 1
   else:
     c.calleeScope = calleeScope
-  c.diagnostics = if diagnostics: @[] else: nil
+  c.diagnostics = if diagnosticsEnabled: @[] else: nil
+  c.diagnosticsEnabled = diagnosticsEnabled
   c.magic = c.calleeSym.magic
   initIdTable(c.bindings)
   if binding != nil and callee.kind in routineKinds:
@@ -717,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     diagnostics: seq[string]
     errorPrefix: string
     flags: TExprFlags = {}
-    collectDiagnostics = m.diagnostics != nil or
+    collectDiagnostics = m.diagnosticsEnabled or
                          sfExplain in typeClass.sym.flags
 
   if collectDiagnostics:
@@ -736,7 +741,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
 
   if collectDiagnostics:
     writelnHook = oldWriteHook
-    for msg in diagnostics: m.diagnostics.safeAdd msg
+    for msg in diagnostics:
+      m.diagnostics.safeAdd msg
+      m.diagnosticsEnabled = true
 
   if checkedBody == nil: return nil
 
@@ -1388,8 +1395,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
 
         var aAsObject = roota.lastSon
 
-        if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind:
-          aAsObject = aAsObject.base
+        if fKind in {tyRef, tyPtr}:
+          if aAsObject.kind == tyObject:
+            # bug #7600, tyObject cannot be passed
+            # as argument to tyRef/tyPtr
+            return isNone
+          elif aAsObject.kind == fKind:
+            aAsObject = aAsObject.base
 
         if aAsObject.kind == tyObject:
           let baseType = aAsObject.base
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 4014d4c58..5413565e6 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -51,15 +51,15 @@ proc parseTopLevelStmt*(p: var TParsers): PNode =
     result = ast.emptyNode
 
 proc utf8Bom(s: string): int =
-  if s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
+  if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF':
     result = 3
   else:
     result = 0
 
 proc containsShebang(s: string, i: int): bool =
-  if s[i] == '#' and s[i+1] == '!':
+  if i+1 < s.len and s[i] == '#' and s[i+1] == '!':
     var j = i + 2
-    while s[j] in Whitespace: inc(j)
+    while j < s.len and s[j] in Whitespace: inc(j)
     result = s[j] == '/'
 
 proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode =
@@ -74,9 +74,9 @@ proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNo
       discard llStreamReadLine(s, line)
       i = 0
       inc linenumber
-    if line[i] == '#' and line[i+1] == '?':
+    if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
       inc(i, 2)
-      while line[i] in Whitespace: inc(i)
+      while i < line.len and line[i] in Whitespace: inc(i)
       var q: TParser
       parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache)
       result = parser.parseAll(q)
diff --git a/compiler/types.nim b/compiler/types.nim
index 5d60fa9b4..edf4e5b46 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -612,13 +612,13 @@ proc firstOrd*(t: PType): BiggestInt =
     else:
       assert(t.n.sons[0].kind == nkSym)
       result = t.n.sons[0].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
     result = firstOrd(lastSon(t))
   of tyOrdinal:
     if t.len > 0: result = firstOrd(lastSon(t))
-    else: internalError("invalid kind for first(" & $t.kind & ')')
+    else: internalError("invalid kind for firstOrd(" & $t.kind & ')')
   else:
-    internalError("invalid kind for first(" & $t.kind & ')')
+    internalError("invalid kind for firstOrd(" & $t.kind & ')')
     result = 0
 
 proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
@@ -651,14 +651,14 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
   of tyEnum:
     assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
     result = t.n.sons[sonsLen(t.n) - 1].sym.position
-  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias:
+  of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
     result = lastOrd(lastSon(t))
   of tyProxy: result = 0
   of tyOrdinal:
     if t.len > 0: result = lastOrd(lastSon(t))
-    else: internalError("invalid kind for last(" & $t.kind & ')')
+    else: internalError("invalid kind for lastOrd(" & $t.kind & ')')
   else:
-    internalError("invalid kind for last(" & $t.kind & ')')
+    internalError("invalid kind for lastOrd(" & $t.kind & ')')
     result = 0
 
 proc lengthOrd*(t: PType): BiggestInt =
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 071cc7706..ba37237e8 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
       result.add mapTypeToAst(t.sons[0], info)
     else:
       result = mapTypeToBracket("ref", mRef, t, info)
-  of tyVar: result = mapTypeToBracket("var", mVar, t, info)
+  of tyVar:
+    if inst:
+      result = newNodeX(nkVarTy)
+      result.add mapTypeToAst(t.sons[0], info)
+    else:
+      result = mapTypeToBracket("var", mVar, t, info)
   of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
   of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
   of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index c3eaf8946..0544dc311 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -1553,6 +1553,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
     result = newNodeIT(nkFloatLit, info, t)
   of tyCString, tyString:
     result = newNodeIT(nkStrLit, info, t)
+    result.strVal = ""
   of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
      tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
     result = newNodeIT(nkNilLit, info, t)