summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--changelog.md27
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/dfa.nim26
-rw-r--r--compiler/main.nim7
-rw-r--r--compiler/msgs.nim6
-rw-r--r--compiler/pragmas.nim2
-rw-r--r--compiler/sem.nim13
-rw-r--r--compiler/semstmts.nim24
-rw-r--r--doc/advopt.txt1
-rw-r--r--lib/js/asyncjs.nim43
-rw-r--r--lib/pure/asyncdispatch.nim2
-rw-r--r--lib/pure/math.nim17
-rw-r--r--lib/pure/unittest.nim91
-rw-r--r--lib/system.nim5
-rw-r--r--lib/system/chcks.nim1
-rw-r--r--lib/system/sysstr.nim8
-rw-r--r--tests/casestmt/tcasestm.nim59
-rw-r--r--tests/js/tasync.nim18
-rw-r--r--tests/pragmas/tnoreturn.nim22
-rw-r--r--tests/stdlib/tfrexp1.nim44
-rw-r--r--tests/stdlib/tstring.nim20
-rw-r--r--tests/stdlib/tunittest.nim38
23 files changed, 419 insertions, 59 deletions
diff --git a/.travis.yml b/.travis.yml
index 095c3ec74..6b8cdbe03 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -48,3 +48,5 @@ script:
   - ./koch csource
   - ./koch nimsuggest
 #  - nim c -r nimsuggest/tester
+  - ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
+  - ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
diff --git a/changelog.md b/changelog.md
index 4d205faf8..5734a4cb1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -102,10 +102,12 @@ This now needs to be written as:
 - Nim's ``rst2html`` command now supports the testing of code snippets via an RST
   extension that we called ``:test:``::
 
+  ```rst
     .. code-block:: nim
         :test:
       # shows how the 'if' statement works
       if true: echo "yes"
+  ```
 - The ``[]`` proc for strings now raises an ``IndexError`` exception when
   the specified slice is out of bounds. See issue
   [#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
@@ -130,7 +132,8 @@ This now needs to be written as:
 - The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
   the new ``rand`` procs. The module now exports the state of the random
   number generator as type ``Rand`` so multiple threads can easily use their
-  own random number generators that do not require locking.
+  own random number generators that do not require locking. For more information
+  about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
 - The compiler is now more consistent in its treatment of ambiguous symbols:
   Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
 - ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
@@ -141,3 +144,25 @@ This now needs to be written as:
 - codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
   function return type placeholders.
 - Asynchronous programming for the JavaScript backend using the `asyncjs` module.
+- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
+  statements after call to noreturn procs are no longer allowed.
+- Noreturn proc calls and raising exceptions branches are now skipped during common type 
+  deduction in if and case expressions. The following code snippets now compile:
+```nim
+import strutils
+let str = "Y"
+let a = case str:
+  of "Y": true
+  of "N": false
+  else: raise newException(ValueError, "Invalid boolean")
+let b = case str:
+  of nil, "": raise newException(ValueError, "Invalid boolean")
+  elif str.startsWith("Y"): true
+  elif str.startsWith("N"): false
+  else: false 
+let c = if str == "Y": true 
+  elif str == "N": false 
+  else:
+    echo "invalid bool" 
+    quit("this is the end")
+```
diff --git a/compiler/commands.nim b/compiler/commands.nim
index de474c6e6..386d7bda8 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -611,7 +611,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "skipparentcfg":
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optSkipParentConfigFiles)
-  of "genscript":
+  of "genscript", "gendeps":
     expectNoArg(switch, arg, pass, info)
     incl(gGlobalOptions, optGenScript)
   of "colors": processOnOffSwitchG({optUseColors}, arg, pass, info)
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 66a71e839..6bb7a03a9 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -344,20 +344,20 @@ proc dfa(code: seq[Instr]) =
     case code[i].kind
     of use, useWithinCall: u[i].incl(code[i].sym.id)
     of def: d[i].incl(code[i].sym.id)
-    of fork:
+    of fork, goto:
       let d = i+code[i].dest
       backrefs.add(d, i)
-    of goto: discard
 
   var w = @[0]
   var maxIters = 50
   var someChange = true
-  while w.len > 0 and maxIters > 0 and someChange:
+  var takenGotos = initIntSet()
+  while w.len > 0 and maxIters > 0: # and someChange:
     dec maxIters
     var pc = w.pop() # w[^1]
     var prevPc = -1
     # this simulates a single linear control flow execution:
-    while pc < code.len and someChange:
+    while pc < code.len:
       # according to the paper, it is better to shrink the working set here
       # in this inner loop:
       #let widx = w.find(pc)
@@ -386,17 +386,21 @@ proc dfa(code: seq[Instr]) =
               if def notin d[prevPc]:
                 excl(intersect, def)
                 someChange = true
+                when defined(debugDfa):
+                  echo "Excluding ", pc, " prev ", prevPc
           assign d[pc], intersect
 
       # our interpretation ![I!]:
       prevPc = pc
+      when defined(debugDfa):
+        echo "looking at ", pc
       case code[pc].kind
       of goto:
         # we must leave endless loops eventually:
-        #if someChange:
-        pc = pc + code[pc].dest
-        #else:
-        #  inc pc
+        if not takenGotos.containsOrIncl(pc) or someChange:
+          pc = pc + code[pc].dest
+        else:
+          inc pc
       of fork:
         # we follow the next instruction but push the dest onto our "work" stack:
         #if someChange:
@@ -405,6 +409,10 @@ proc dfa(code: seq[Instr]) =
       of use, useWithinCall, def:
         inc pc
 
+  when defined(useDfa) and defined(debugDfa):
+    for i in 0..<code.len:
+      echo "PC ", i, ": defs: ", d[i], "; uses ", u[i]
+
   # now check the condition we're interested in:
   for i in 0..<code.len:
     case code[i].kind
@@ -508,7 +516,7 @@ proc dfaUnused(code: seq[Instr]) =
 proc dataflowAnalysis*(s: PSym; body: PNode) =
   var c = Con(code: @[], blocks: @[])
   gen(c, body)
-  #echoCfg(c.code)
+  when defined(useDfa) and defined(debugDfa): echoCfg(c.code)
   dfa(c.code)
 
 proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
diff --git a/compiler/main.nim b/compiler/main.nim
index 1e94a6ca0..08fc4b138 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -16,7 +16,7 @@ import
   cgen, jsgen, json, nversion,
   platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
   docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
-  modulegraphs
+  modulegraphs, tables
 
 from magicsys import systemModule, resetSysTypes
 
@@ -36,6 +36,9 @@ proc writeDepsFile(g: ModuleGraph; project: string) =
   for m in g.modules:
     if m != nil:
       f.writeLine(toFullPath(m.position.int32))
+  for k in g.inclToMod.keys:
+    if g.getModule(k).isNil:  # don't repeat includes which are also modules
+      f.writeLine(k.toFullPath)
   f.close()
 
 proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
@@ -77,6 +80,8 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
     let proj = changeFileExt(gProjectFull, "")
     extccomp.callCCompiler(proj)
     extccomp.writeJsonBuildInstructions(proj)
+    if optGenScript in gGlobalOptions:
+      writeDepsFile(graph, toGeneratedFile(proj, ""))
 
 proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
   let proj = changeFileExt(gProjectFull, "")
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 2668c72ae..4e6226122 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -26,7 +26,8 @@ type
     errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
     errExceptionExpected, errExceptionAlreadyHandled,
     errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
-    errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
+    errInvalidNumberOfYieldExpr, errCannotReturnExpr, 
+    errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
     errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
     errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
     errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
@@ -179,8 +180,9 @@ const
     errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
     errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
     errCannotReturnExpr: "current routine cannot return an expression",
+    errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
     errAttemptToRedefine: "redefinition of \'$1\'",
-    errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'",
+    errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\', \'continue\' or proc call with noreturn pragma",
     errStmtExpected: "statement expected",
     errInvalidLabel: "\'$1\' is no label",
     errInvalidCmdLineOption: "invalid command line option: \'$1\'",
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index b598cadb2..02b57d5a3 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -771,6 +771,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
       of wNoreturn:
         noVal(it)
         incl(sym.flags, sfNoReturn)
+        if sym.typ[0] != nil:
+          localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed)
       of wDynlib:
         processDynLib(c, it, sym)
       of wCompilerproc:
diff --git a/compiler/sem.nim b/compiler/sem.nim
index bc994201d..ababbd303 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -165,6 +165,19 @@ proc commonType*(x, y: PType): PType =
         result = newType(k, r.owner)
         result.addSonSkipIntLit(r)
 
+proc endsInNoReturn(n: PNode): bool =
+  # check if expr ends in raise exception or call of noreturn proc
+  var it = n
+  while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: 
+    it = it.lastSon
+  result = it.kind == nkRaiseStmt or 
+    it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+
+proc commonType*(x: PType, y: PNode): PType =
+  # ignore exception raising branches in case/if expressions
+  if endsInNoReturn(y): return x
+  commonType(x, y.typ)
+
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
   result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
   when defined(nimsuggest):
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 8ed120c98..b1fa8c19b 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -165,14 +165,14 @@ proc semIf(c: PContext, n: PNode): PNode =
       it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
       when not newScopeForIf: openScope(c)
       it.sons[1] = semExprBranch(c, it.sons[1])
-      typ = commonType(typ, it.sons[1].typ)
+      typ = commonType(typ, it.sons[1])
       closeScope(c)
     elif it.len == 1:
       hasElse = true
       it.sons[0] = semExprBranchScope(c, it.sons[0])
-      typ = commonType(typ, it.sons[0].typ)
+      typ = commonType(typ, it.sons[0])
     else: illFormedAst(it)
-  if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
+  if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
     for it in n: discardCheck(c, it.lastSon)
     result.kind = nkIfStmt
     # propagate any enforced VoidContext:
@@ -180,7 +180,8 @@ proc semIf(c: PContext, n: PNode): PNode =
   else:
     for it in n:
       let j = it.len-1
-      it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
+      if not endsInNoReturn(it.sons[j]):
+        it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
     result.kind = nkIfExpr
     result.typ = typ
 
@@ -213,7 +214,7 @@ proc semCase(c: PContext, n: PNode): PNode =
       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].typ)
+      typ = commonType(typ, x.sons[last])
     of nkElifBranch:
       chckCovered = false
       checkSonsLen(x, 2)
@@ -221,13 +222,13 @@ proc semCase(c: PContext, n: PNode): PNode =
       x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
       when not newScopeForIf: openScope(c)
       x.sons[1] = semExprBranch(c, x.sons[1])
-      typ = commonType(typ, x.sons[1].typ)
+      typ = commonType(typ, x.sons[1])
       closeScope(c)
     of nkElse:
       chckCovered = false
       checkSonsLen(x, 1)
       x.sons[0] = semExprBranchScope(c, x.sons[0])
-      typ = commonType(typ, x.sons[0].typ)
+      typ = commonType(typ, x.sons[0])
       hasElse = true
     else:
       illFormedAst(x)
@@ -237,7 +238,7 @@ proc semCase(c: PContext, n: PNode): PNode =
     else:
       localError(n.info, errNotAllCasesCovered)
   closeScope(c)
-  if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
+  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 == enforceVoidContext:
@@ -246,7 +247,8 @@ proc semCase(c: PContext, n: PNode): PNode =
     for i in 1..n.len-1:
       var it = n.sons[i]
       let j = it.len-1
-      it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
+      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 =
@@ -1851,8 +1853,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
       else:
         n.typ = n.sons[i].typ
         if not isEmptyType(n.typ): n.kind = nkStmtListExpr
-      case n.sons[i].kind
-      of LastBlockStmts:
+      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,
diff --git a/doc/advopt.txt b/doc/advopt.txt
index ab10d65ba..a1210118e 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -37,6 +37,7 @@ Advanced options:
   --noMain                  do not generate a main procedure
   --genScript               generate a compile script (in the 'nimcache'
                             subdirectory named 'compile_$project$scriptext')
+  --genDeps                 generate a '.deps' file containing the dependencies
   --os:SYMBOL               set the target operating system (cross-compilation)
   --cpu:SYMBOL              set the target processor (cross-compilation)
   --debuginfo               enables debug information
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
index bde3d787f..ec410ee39 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -44,10 +44,10 @@
 ##         resolve(game)
 ##     return promise
 ##
-## Forward definitions work properly, you just don't need to add the ``{.async.}`` pragma:
+## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma:
 ##
 ## .. code-block:: nim
-##   proc loadGame(name: string): Future[Game]
+##   proc loadGame(name: string): Future[Game] {.async.}
 ##
 ## JavaScript compatibility
 ## ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -83,23 +83,54 @@ proc replaceReturn(node: var NimNode) =
       replaceReturn(son)
     inc z
 
+proc isFutureVoid(node: NimNode): bool =
+  result = node.kind == nnkBracketExpr and
+           node[0].kind == nnkIdent and $node[0] == "Future" and
+           node[1].kind == nnkIdent and $node[1] == "void"
+
 proc generateJsasync(arg: NimNode): NimNode =
   assert arg.kind == nnkProcDef
   result = arg
+  var isVoid = false
+  var jsResolveNode = ident("jsResolve")
+
   if arg.params[0].kind == nnkEmpty:
     result.params[0] = nnkBracketExpr.newTree(ident("Future"), ident("void"))
+    isVoid = true
+  elif isFutureVoid(arg.params[0]):
+    isVoid = true
+
   var code = result.body
   replaceReturn(code)
   result.body = nnkStmtList.newTree()
-  var q = quote:
-    proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
-    proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
-  result.body.add(q)
+
+  if len(code) > 0:
+    var awaitFunction = quote:
+      proc await[T](f: Future[T]): T {.importcpp: "(await #)".}
+    result.body.add(awaitFunction)
+
+    var resolve: NimNode
+    if isVoid:
+      resolve = quote:
+        var `jsResolveNode` {.importcpp: "undefined".}: Future[void]
+    else:
+      resolve = quote:
+        proc jsResolve[T](a: T): Future[T] {.importcpp: "#".}
+    result.body.add(resolve)
+  else:
+    result.body = newEmptyNode()
   for child in code:
     result.body.add(child)
+
+  if len(code) > 0 and isVoid:
+    var voidFix = quote:
+      return `jsResolveNode`
+    result.body.add(voidFix)
+
   result.pragma = quote:
     {.codegenDecl: "async function $2($3)".}
 
+
 macro async*(arg: untyped): untyped =
   ## Macro which converts normal procedures into
   ## javascript-compatible async procedures
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 675e8fc5e..23eb80b37 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -1234,7 +1234,7 @@ else:
           processBasicCallbacks(fd, writeList)
           result = true
 
-        if Event.User in events or events == {Event.Error}:
+        if Event.User in events:
           processBasicCallbacks(fd, readList)
           custom = true
           if rLength == 0:
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index a9dabfa48..cbd04a145 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -351,15 +351,19 @@ proc round*[T: float32|float64](x: T, places: int = 0): T =
     result = round0(x*mult)/mult
 
 when not defined(JS):
-  proc frexp*(x: float32, exponent: var int): float32 {.
+  proc c_frexp*(x: float32, exponent: var int32): float32 {.
     importc: "frexp", header: "<math.h>".}
-  proc frexp*(x: float64, exponent: var int): float64 {.
+  proc c_frexp*(x: float64, exponent: var int32): float64 {.
     importc: "frexp", header: "<math.h>".}
+  proc frexp*[T, U](x: T, exponent: var U): T =
     ## Split a number into mantissa and exponent.
     ## `frexp` calculates the mantissa m (a float greater than or equal to 0.5
     ## and less than 1) and the integer value n such that `x` (the original
     ## float value) equals m * 2**n. frexp stores n in `exponent` and returns
     ## m.
+    var exp: int32
+    result = c_frexp(x, exp)
+    exponent = exp
 else:
   proc frexp*[T: float32|float64](x: T, exponent: var int): T =
     if x == 0.0:
@@ -368,9 +372,14 @@ else:
     elif x < 0.0:
       result = -frexp(-x, exponent)
     else:
-      var ex = floor(log2(x))
-      exponent = round(ex)
+      var ex = trunc(log2(x))
+      exponent = int(ex)
       result = x / pow(2.0, ex)
+      if abs(result) >= 1:
+        inc(exponent)
+        result = result / 2
+      if exponent == 1024 and result == 0.0:
+        result = 0.99999999999999988898
 
 proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] =
   ## Breaks `x` into an integral and a fractional part.
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 7a8d1dad0..fbce087ff 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -21,13 +21,41 @@
 ## ``nim c -r <testfile.nim>`` exits with 0 or 1
 ##
 ## Running a single test
-## ---------------------
+## =====================
 ##
-## Simply specify the test name as a command line argument.
+## Specify the test name as a command line argument.
 ##
 ## .. code::
 ##
-##   nim c -r test "my super awesome test name"
+##   nim c -r test "my test name" "another test"
+##
+## Multiple arguments can be used.
+##
+## Running a single test suite
+## ===========================
+##
+## Specify the suite name delimited by ``"::"``.
+##
+## .. code::
+##
+##   nim c -r test "my test name::"
+##
+## Selecting tests by pattern
+## ==========================
+##
+## A single ``"*"`` can be used for globbing.
+##
+## Delimit the end of a suite name with ``"::"``.
+##
+## Tests matching **any** of the arguments are executed.
+##
+## .. code::
+##
+##   nim c -r test fast_suite::mytest1 fast_suite::mytest2
+##   nim c -r test "fast_suite::mytest*"
+##   nim c -r test "auth*::" "crypto::hashing*"
+##   # Run suites starting with 'bug #' and standalone tests starting with '#'
+##   nim c -r test 'bug #*::' '::#*'
 ##
 ## Example
 ## -------
@@ -121,7 +149,7 @@ var
 
   checkpoints {.threadvar.}: seq[string]
   formatters {.threadvar.}: seq[OutputFormatter]
-  testsToRun {.threadvar.}: HashSet[string]
+  testsFilters {.threadvar.}: HashSet[string]
 
 when declared(stdout):
   abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
@@ -300,22 +328,63 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
 method suiteEnded*(formatter: JUnitOutputFormatter) =
   formatter.stream.writeLine("\t</testsuite>")
 
-proc shouldRun(testName: string): bool =
-  if testsToRun.len == 0:
+proc glob(matcher, filter: string): bool =
+  ## Globbing using a single `*`. Empty `filter` matches everything.
+  if filter.len == 0:
     return true
 
-  result = testName in testsToRun
+  if not filter.contains('*'):
+    return matcher == filter
+
+  let beforeAndAfter = filter.split('*', maxsplit=1)
+  if beforeAndAfter.len == 1:
+    # "foo*"
+    return matcher.startswith(beforeAndAfter[0])
+
+  if matcher.len < filter.len - 1:
+    return false  # "12345" should not match "123*345"
+
+  return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
+
+proc matchFilter(suiteName, testName, filter: string): bool =
+  if filter == "":
+    return true
+  if testName == filter:
+    # corner case for tests containing "::" in their name
+    return true
+  let suiteAndTestFilters = filter.split("::", maxsplit=1)
+
+  if suiteAndTestFilters.len == 1:
+    # no suite specified
+    let test_f = suiteAndTestFilters[0]
+    return glob(testName, test_f)
+
+  return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
+
+when defined(testing): export matchFilter
+
+proc shouldRun(currentSuiteName, testName: string): bool =
+  ## Check if a test should be run by matching suiteName and testName against
+  ## test filters.
+  if testsFilters.len == 0:
+    return true
+
+  for f in testsFilters:
+    if matchFilter(currentSuiteName, testName, f):
+      return true
+
+  return false
 
 proc ensureInitialized() =
   if formatters == nil:
     formatters = @[OutputFormatter(defaultConsoleFormatter())]
 
-  if not testsToRun.isValid:
-    testsToRun.init()
+  if not testsFilters.isValid:
+    testsFilters.init()
     when declared(paramCount):
       # Read tests to run from the command line.
       for i in 1 .. paramCount():
-        testsToRun.incl(paramStr(i))
+        testsFilters.incl(paramStr(i))
 
 # These two procs are added as workarounds for
 # https://github.com/nim-lang/Nim/issues/5549
@@ -395,7 +464,7 @@ template test*(name, body) {.dirty.} =
 
   ensureInitialized()
 
-  if shouldRun(name):
+  if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
     checkpoints = @[]
     var testStatusIMPL {.inject.} = OK
 
diff --git a/lib/system.nim b/lib/system.nim
index 83e87683a..85643891b 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2916,7 +2916,10 @@ when not defined(JS): #and not defined(nimscript):
       elif x > y: result = 1
       else: result = 0
     else:
-      result = int(c_strcmp(x, y))
+      let minlen = min(x.len, y.len)
+      result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
+      if result == 0:
+        result = x.len - y.len
 
   when defined(nimscript):
     proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index 1520f231e..69b680dbd 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
   while x != subclass:
     if x == nil:
       sysFatal(ObjectConversionError, "invalid object conversion")
-      break
     x = x.base
 
 proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 56b8ade97..4c5f3d9a1 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -24,10 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
   if a == b: return 0
   if a == nil: return -1
   if b == nil: return 1
-  when defined(nimNoArrayToCstringConversion):
-    return c_strcmp(addr a.data, addr b.data)
-  else:
-    return c_strcmp(a.data, b.data)
+  let minlen = min(a.len, b.len)
+  result = c_memcmp(addr a.data, addr b.data, minlen.csize)
+  if result == 0:
+    result = a.len - b.len
 
 proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
   if a == b: return true
diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim
index 7ac20bf2f..b005d8120 100644
--- a/tests/casestmt/tcasestm.nim
+++ b/tests/casestmt/tcasestm.nim
@@ -36,5 +36,64 @@ var z = case i
 echo z
 #OUT ayyy
 
+let str1 = "Y"
+let str2 = "NN"
+let a = case str1:
+  of "Y": true
+  of "N": false
+  else: 
+    echo "no good"
+    quit("quiting")
 
+let b = case str2:
+  of nil, "": raise newException(ValueError, "Invalid boolean")
+  elif str2[0] == 'Y': true
+  elif str2[0] == 'N': false
+  else: "error".quit(2)
 
+doAssert(a == true)
+doAssert(b == false)
+
+var bb: bool
+doassert(not compiles(
+  bb = case str2:
+    of nil, "": raise newException(ValueError, "Invalid boolean")
+    elif str.startsWith("Y"): true
+    elif str.startsWith("N"): false
+))
+
+doassert(not compiles(
+  bb = case str2:
+    of "Y": true
+    of "N": false
+))
+
+doassert(not compiles(
+  bb = case str2:
+    of "Y": true
+    of "N": raise newException(ValueError, "N not allowed")
+))
+
+doassert(not compiles(
+  bb = case str2:
+    of "Y": raise newException(ValueError, "Invalid Y")
+    else: raise newException(ValueError, "Invalid N")
+))
+
+
+doassert(not compiles(
+  bb = case str2:
+    of "Y":
+      raise newException(ValueError, "Invalid Y")
+      true    
+    else: raise newException(ValueError, "Invalid")
+))
+
+
+doassert(not compiles(
+  bb = case str2:
+    of "Y":
+      "invalid Y".quit(3)
+      true    
+    else: raise newException(ValueError, "Invalid")
+))
\ No newline at end of file
diff --git a/tests/js/tasync.nim b/tests/js/tasync.nim
index a164827d2..34ef97b8b 100644
--- a/tests/js/tasync.nim
+++ b/tests/js/tasync.nim
@@ -1,8 +1,8 @@
 discard """
   disabled: true
   output: '''
-0
 x
+e
 '''
 """
 
@@ -10,16 +10,22 @@ import asyncjs
 
 # demonstrate forward definition
 # for js
-proc y(e: int): Future[string]
+proc y(e: int): Future[string] {.async.}
 
-proc x(e: int) {.async.} =
+proc e: int {.discardable.} =
+  echo "e"
+  return 2
+
+proc x(e: int): Future[void] {.async.} =
   var s = await y(e)
   echo s
+  e()
 
 proc y(e: int): Future[string] {.async.} =
-  echo 0
-  return "x"
-
+  if e > 0:
+    return await y(0)
+  else:
+    return "x"
 
 
 discard x(2)
diff --git a/tests/pragmas/tnoreturn.nim b/tests/pragmas/tnoreturn.nim
new file mode 100644
index 000000000..4d00c6034
--- /dev/null
+++ b/tests/pragmas/tnoreturn.nim
@@ -0,0 +1,22 @@
+discard """
+ccodeCheck: "\\i @'__attribute__((noreturn))' .*"
+"""
+
+proc noret1*(i: int) {.noreturn.} = 
+  echo i
+
+
+proc noret2*(i: int): void {.noreturn.} = 
+  echo i
+
+var p {.used.}: proc(i: int): int
+doAssert(not compiles(
+  p = proc(i: int): int {.noreturn.} = i # noreturn lambda returns int
+))
+
+
+doAssert(not compiles(
+  block:
+    noret1(5)
+    echo 1 # statement after noreturn
+))
diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim
new file mode 100644
index 000000000..c6bb2b38c
--- /dev/null
+++ b/tests/stdlib/tfrexp1.nim
@@ -0,0 +1,44 @@
+discard """
+  targets: "js c c++"
+  output: '''ok'''
+"""
+
+import math
+import strformat
+
+const manualTest = false
+
+proc frexp_test(lo, hi, step: float64) =
+  var exp: int
+  var frac: float64
+
+  var eps = 1e-15.float64
+
+  var x:float64 = lo
+  while x <= hi:
+    frac = frexp(x.float, exp)
+    let rslt = pow(2.0, float(exp)) * frac
+
+    doAssert(abs(rslt - x) < eps)
+
+    when manualTest:
+      echo fmt("x: {x:10.3f} exp: {exp:4d} frac: {frac:24.20f} check: {$(abs(rslt - x) < eps):-5s} {rslt: 9.3f}")
+    x += step
+
+when manualTest:
+  var exp: int
+  var frac: float64
+
+  for flval in [1.7976931348623157e+308, -1.7976931348623157e+308, # max, min float64
+                3.4028234663852886e+38, -3.4028234663852886e+38,   # max, min float32
+                4.9406564584124654e-324, -4.9406564584124654e-324, # smallest/largest positive/negative float64
+                1.4012984643248171e-45, -1.4012984643248171e-45,   # smallest/largest positive/negative float32
+                2.2250738585072014e-308, 1.1754943508222875e-38]:  # smallest normal float64/float32
+    frac = frexp(flval, exp)
+    echo fmt("{flval:25.16e}, {exp: 6d}, {frac: .20f} {frac * pow(2.0, float(exp)): .20e}")
+
+  frexp_test(-1000.0, 1000.0, 0.0125)
+else:
+  frexp_test(-1000000.0, 1000000.0, 0.125)
+
+echo "ok"
diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim
index 904bc462a..660746150 100644
--- a/tests/stdlib/tstring.nim
+++ b/tests/stdlib/tstring.nim
@@ -56,4 +56,24 @@ proc test_string_slice() =
 
   echo("OK")
 
+proc test_string_cmp() =
+  let world = "hello\0world"
+  let earth = "hello\0earth"
+  let short = "hello\0"
+  let hello = "hello"
+  let goodbye = "goodbye"
+
+  doAssert world == world
+  doAssert world != earth
+  doAssert world != short
+  doAssert world != hello
+  doAssert world != goodbye
+
+  doAssert cmp(world, world) == 0
+  doAssert cmp(world, earth) > 0
+  doAssert cmp(world, short) > 0
+  doAssert cmp(world, hello) > 0
+  doAssert cmp(world, goodbye) > 0
+
 test_string_slice()
+test_string_cmp()
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index e4a801871..86b9fd037 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -13,6 +13,8 @@ discard """
 
 [Suite] bug #5784
 
+[Suite] test name filtering
+
 '''
 """
 
@@ -120,3 +122,39 @@ suite "bug #5784":
       field: int
     var obj: Obj
     check obj.isNil or obj.field == 0
+
+when defined(testing):
+  suite "test name filtering":
+    test "test name":
+      check matchFilter("suite1", "foo", "")
+      check matchFilter("suite1", "foo", "foo")
+      check matchFilter("suite1", "foo", "::")
+      check matchFilter("suite1", "foo", "*")
+      check matchFilter("suite1", "foo", "::foo")
+      check matchFilter("suite1", "::foo", "::foo")
+
+    test "test name - glob":
+      check matchFilter("suite1", "foo", "f*")
+      check matchFilter("suite1", "foo", "*oo")
+      check matchFilter("suite1", "12345", "12*345")
+      check matchFilter("suite1", "q*wefoo", "q*wefoo")
+      check false == matchFilter("suite1", "foo", "::x")
+      check false == matchFilter("suite1", "foo", "::x*")
+      check false == matchFilter("suite1", "foo", "::*x")
+      #  overlap
+      check false == matchFilter("suite1", "12345", "123*345")
+      check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f")
+
+    test "suite name":
+      check matchFilter("suite1", "foo", "suite1::")
+      check false == matchFilter("suite1", "foo", "suite2::")
+      check matchFilter("suite1", "qwe::foo", "qwe::foo")
+      check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo")
+
+    test "suite name - glob":
+      check matchFilter("suite1", "foo", "::*")
+      check matchFilter("suite1", "foo", "*::*")
+      check matchFilter("suite1", "foo", "*::foo")
+      check false == matchFilter("suite1", "foo", "*ite2::")
+      check matchFilter("suite1", "q**we::foo", "q**we::foo")
+      check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e")