summary refs log tree commit diff stats
path: root/lib/std
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/assertions.nim121
-rw-r--r--lib/std/enumutils.nim4
-rw-r--r--lib/std/jsonutils.nim3
-rw-r--r--lib/std/packedsets.nim3
-rw-r--r--lib/std/private/globs.nim4
-rw-r--r--lib/std/sha1.nim3
6 files changed, 138 insertions, 0 deletions
diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim
new file mode 100644
index 000000000..3d2112b1a
--- /dev/null
+++ b/lib/std/assertions.nim
@@ -0,0 +1,121 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements assertion handling.
+
+when not declared(sysFatal):
+  include "system/fatal"
+
+import std/private/miscdollars
+# ---------------------------------------------------------------------------
+# helpers
+
+type InstantiationInfo = tuple[filename: string, line: int, column: int]
+
+proc `$`(info: InstantiationInfo): string =
+  # The +1 is needed here
+  # instead of overriding `$` (and changing its meaning), consider explicit name.
+  result = ""
+  result.toLocation(info.filename, info.line, info.column + 1)
+
+# ---------------------------------------------------------------------------
+
+when not defined(nimHasSinkInference):
+  {.pragma: nosinks.}
+
+proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} =
+  ## Raises an `AssertionDefect` with `msg`.
+  sysFatal(AssertionDefect, msg)
+
+proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
+  ## Raises an `AssertionDefect` with `msg`, but this is hidden
+  ## from the effect system. Called when an assertion failed.
+  # trick the compiler to not list `AssertionDefect` when called
+  # by `assert`.
+  # xxx simplify this pending bootstrap >= 1.4.0, after which cast not needed
+  # anymore since `Defect` can't be raised.
+  type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, tags: [].}
+  cast[Hide](raiseAssert)(msg)
+
+template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) =
+  when enabled:
+    const
+      loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace"))
+      ploc = $loc
+    bind instantiationInfo
+    mixin failedAssertImpl
+    {.line: loc.}:
+      if not cond:
+        failedAssertImpl(ploc & " `" & expr & "` " & msg)
+
+template assert*(cond: untyped, msg = "") =
+  ## Raises `AssertionDefect` with `msg` if `cond` is false. Note
+  ## that `AssertionDefect` is hidden from the effect system, so it doesn't
+  ## produce `{.raises: [AssertionDefect].}`. This exception is only supposed
+  ## to be caught by unit testing frameworks.
+  ##
+  ## No code will be generated for `assert` when passing `-d:danger` (implied by `--assertions:off`).
+  ## See `command line switches <nimc.html#compiler-usage-commandminusline-switches>`_.
+  runnableExamples: assert 1 == 1
+  runnableExamples("--assertions:off"):
+    assert 1 == 2 # no code generated, no failure here
+  runnableExamples("-d:danger"): assert 1 == 2 # ditto
+  assertImpl(cond, msg, astToStr(cond), compileOption("assertions"))
+
+template doAssert*(cond: untyped, msg = "") =
+  ## Similar to `assert <#assert.t,untyped,string>`_ but is always turned on regardless of `--assertions`.
+  runnableExamples:
+    doAssert 1 == 1 # generates code even when built with `-d:danger` or `--assertions:off`
+  assertImpl(cond, msg, astToStr(cond), true)
+
+template onFailedAssert*(msg, code: untyped): untyped {.dirty.} =
+  ## Sets an assertion failure handler that will intercept any assert
+  ## statements following `onFailedAssert` in the current scope.
+  runnableExamples:
+    type MyError = object of CatchableError
+      lineinfo: tuple[filename: string, line: int, column: int]
+    # block-wide policy to change the failed assert exception type in order to
+    # include a lineinfo
+    onFailedAssert(msg):
+      raise (ref MyError)(msg: msg, lineinfo: instantiationInfo(-2))
+    doAssertRaises(MyError): doAssert false
+  template failedAssertImpl(msgIMPL: string): untyped {.dirty.} =
+    let msg = msgIMPL
+    code
+
+template doAssertRaises*(exception: typedesc, code: untyped) =
+  ## Raises `AssertionDefect` if specified `code` does not raise `exception`.
+  runnableExamples:
+    doAssertRaises(ValueError): raise newException(ValueError, "Hello World")
+    doAssertRaises(CatchableError): raise newException(ValueError, "Hello World")
+    doAssertRaises(AssertionDefect): doAssert false
+  var wrong = false
+  const begin = "expected raising '" & astToStr(exception) & "', instead"
+  const msgEnd = " by: " & astToStr(code)
+  template raisedForeign = raiseAssert(begin & " raised foreign exception" & msgEnd)
+  when Exception is exception:
+    try:
+      if true:
+        code
+      wrong = true
+    except Exception as e: discard
+    except: raisedForeign()
+  else:
+    try:
+      if true:
+        code
+      wrong = true
+    except exception:
+      discard
+    except Exception as e:
+      mixin `$` # alternatively, we could define $cstring in this module
+      raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd)
+    except: raisedForeign()
+  if wrong:
+    raiseAssert(begin & " nothing was raised" & msgEnd)
diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim
index b7d2b9f89..9d4ff1bcf 100644
--- a/lib/std/enumutils.nim
+++ b/lib/std/enumutils.nim
@@ -10,6 +10,10 @@
 import macros
 from typetraits import OrdinalEnum, HoleyEnum
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
 # xxx `genEnumCaseStmt` needs tests and runnableExamples
 
 macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim
index 19384b5d1..722ea49b5 100644
--- a/lib/std/jsonutils.nim
+++ b/lib/std/jsonutils.nim
@@ -34,6 +34,9 @@ import macros
 from enumutils import symbolName
 from typetraits import OrdinalEnum
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 when not defined(nimFixedForwardGeneric):
   # xxx remove pending csources_v1 update >= 1.2.0
   proc to[T](node: JsonNode, t: typedesc[T]): T =
diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim
index b2ee917eb..1e2892658 100644
--- a/lib/std/packedsets.nim
+++ b/lib/std/packedsets.nim
@@ -24,6 +24,9 @@
 import std/private/since
 import hashes
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type
   BitScalar = uint
 
diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim
index 190316f93..28a810372 100644
--- a/lib/std/private/globs.nim
+++ b/lib/std/private/globs.nim
@@ -8,6 +8,10 @@ import os
 when defined(windows):
   from strutils import replace
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+
 when defined(nimHasEffectsOf):
   {.experimental: "strictEffects".}
 else:
diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim
index ed962707b..50175024c 100644
--- a/lib/std/sha1.nim
+++ b/lib/std/sha1.nim
@@ -29,6 +29,9 @@ runnableExamples("-r:off"):
 import strutils
 from endians import bigEndian32, bigEndian64
 
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
 const Sha1DigestSize = 20
 
 type