Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add std/asserts containing enforce; (catchable + customizable doAssert ) #15606

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@

- Added `dom.scrollIntoView` proc with options

- add `std/exceptions` containing `enforce`

## Language changes

- `nimscript` now handles `except Exception as e`.
Expand Down
28 changes: 28 additions & 0 deletions lib/std/exceptions.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import system/assertions
import std/private/miscdollars

proc onEnforceFail[T](typ: typedesc, prefix: string, arg: T) {.noreturn, noinline.} =
## Making this a proc reduces size of binaries
raise newException(typ, prefix & $arg)

template enforce*[T](cond: untyped, arg: T, typ: typedesc = EnforceError) =
## similar to `doAssert` but defaults to raising catchable exception
## instead of `AssertionDefect`, and allows customizing the raised exception type.
# `-d:nimLeanMessages` further reduces size of binaries at expense of not
# showing location information; in future we can avoid generating un-necessary
# strings and forward `TLineInfo` directly (or some equivalent compact type),
# and then defer the string rendering until needed in `onEnforceFail`,
# reducing binary size while preserving location information. stacktraces
# can benefit from the same optimization.
runnableExamples:
let a = 1
enforce a == 1, $(a,)
doAssertRaises(EnforceError): enforce a == 2, $(a,)
doAssertRaises(ValueError): enforce a == 2, $(a,), ValueError
const loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace"))
{.line: loc.}:
if not cond:
const prefix =
when defined(nimLeanMessages): ""
else: $loc & " `" & astToStr(cond) & "` "
onEnforceFail(typ, prefix, arg)
10 changes: 10 additions & 0 deletions lib/std/private/miscdollars.nim
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ template toLocation*(result: var string, file: string | cstring, line: int, col:
when declared(addInt): result.addInt col
else: result.add $col
result.add ")"

type InstantiationInfo* = tuple[filename: string, line: int, column: int]

proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.}

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)
2 changes: 1 addition & 1 deletion lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ type
RootRef* = ref RootObj ## Reference to `RootObj`.


include "system/exceptions"
include "system/exceptions_impl"

when defined(js) or defined(nimdoc):
type
Expand Down
16 changes: 3 additions & 13 deletions lib/system/assertions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,13 @@
##
## **Note:** This module is reexported by `system` and thus does not need to be
## imported directly (with `system/assertions`).
##
## See also: `std/exceptions`

when not declared(sysFatal):
include "system/fatal"

import std/private/miscdollars
# ---------------------------------------------------------------------------
# helpers

type InstantiationInfo = tuple[filename: string, line: int, column: int]

proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.}
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.}
Expand Down Expand Up @@ -94,6 +83,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) =
const begin = "expected raising '" & astToStr(exception) & "', instead"
const msgEnd = " by: " & astToStr(code)
template raisedForeign = raiseAssert(begin & " raised foreign exception" & msgEnd)
mixin `$` # for `tests/test_nimscript.nims` (import assertions appears before dollars)
when Exception is exception:
try:
if true:
Expand Down
2 changes: 2 additions & 0 deletions lib/system/exceptions.nim → lib/system/exceptions_impl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type

CatchableError* = object of Exception ## \
## Abstract class for all exceptions that are catchable.
EnforceError* = object of Exception ## \
## Default exception raised by `asserts.enforce`.
IOError* = object of CatchableError ## \
## Raised if an IO error occurred.
EOFError* = object of IOError ## \
Expand Down
21 changes: 21 additions & 0 deletions tests/stdlib/texceptions.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#[
see also tassert2
]#

import std/exceptions
import std/strutils



# line 10
block: ## checks AST isn't transformed as it used to
let a = 1
enforce a == 1, $a
var raised = false
try:
enforce a > 1, $a
except EnforceError as e:
raised = true
doAssert e.msg.endsWith "texceptions.nim(16, 13) `a > 1` 1"
doAssert raised
doAssertRaises(EnforceError): enforce a > 1, $a