Skip to content

Commit 26b86c8

Browse files
authored
Makes except: panics on Defect (#24821)
implements nim-lang/RFCs#557 It inserts defect handing into a bare except branch ```nim try: raiseAssert "test" except: echo "nope" ``` => ```nim try: raiseAssert "test" except: # New behaviov, now well-defined: **never** catches the assert, regardless of panic mode raiseDefect() echo "nope" ``` In this way, `except` still catches foreign exceptions, but panics on `Defect`. Probably when Nim has `except {.foreign.}`, we can extend `raiseDefect` to foreign exceptions as well. That's supposed to be a small use case anyway. `--legacy:noPanicOnExcept` is provided for a transition period.
1 parent 2ed45eb commit 26b86c8

File tree

16 files changed

+85
-18
lines changed

16 files changed

+85
-18
lines changed

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ errors.
1919

2020
- With `-d:nimPreviewAsmSemSymbol`, backticked symbols are type checked in the `asm/emit` statements.
2121

22+
- The bare `except:` now panics on `Defect`. Use `except Exception:` or `except Defect:` to catch `Defect`. `--legacy:noPanicOnExcept` is provided for a transition period.
23+
2224
## Standard library additions and changes
2325

2426
[//]: # "Additions:"

compiler/options.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ type
248248
## Useful for libraries that rely on local passC
249249
jsNoLambdaLifting
250250
## Old transformation for closures in JS backend
251+
noPanicOnExcept
252+
## don't panic on bare except
251253

252254
SymbolFilesOption* = enum
253255
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest

compiler/transf.nim

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,23 @@ proc transformCall(c: PTransf, n: PNode): PNode =
957957
else:
958958
result = s
959959

960+
proc transformBareExcept(c: PTransf, n: PNode): PNode =
961+
result = newTransNode(nkExceptBranch, n, 1)
962+
if isEmptyType(n[0].typ):
963+
result[0] = newNodeI(nkStmtList, n[0].info)
964+
else:
965+
result[0] = newNodeIT(nkStmtListExpr, n[0].info, n[0].typ)
966+
# Generating `raiseDefect()`
967+
let raiseDefectCall = callCodegenProc(c.graph, "raiseDefect", n[0].info)
968+
result[0].add raiseDefectCall
969+
if n[0].kind in {nkStmtList, nkStmtListExpr}:
970+
# flattens stmtList
971+
for son in n[0]:
972+
result[0].add son
973+
else:
974+
result[0].add n[0]
975+
result[0] = transform(c, result[0])
976+
960977
proc transformExceptBranch(c: PTransf, n: PNode): PNode =
961978
if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
962979
let excTypeNode = n[0][1]
@@ -985,6 +1002,9 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
9851002
# Replace the `Exception as foobar` with just `Exception`.
9861003
result[0] = transform(c, n[0][1])
9871004
result[1] = actions
1005+
elif n.len == 1 and
1006+
noPanicOnExcept notin c.graph.config.legacyFeatures:
1007+
result = transformBareExcept(c, n)
9881008
else:
9891009
result = transformSons(c, n)
9901010

compiler/vmops.nim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} =
143143
proc getCurrentExceptionWrapper(a: VmArgs) {.nimcall.} =
144144
setResult(a, a.currentException)
145145

146+
proc raiseDefectWrapper(a: VmArgs) {.nimcall.} =
147+
discard
148+
146149
proc staticWalkDirImpl(path: string, relative: bool): PNode =
147150
result = newNode(nkBracket)
148151
for k, f in walkDir(path, relative):
@@ -263,6 +266,7 @@ proc registerAdditionalOps*(c: PCtx) =
263266
wrap2si(readLines, ioop)
264267
systemop getCurrentExceptionMsg
265268
systemop getCurrentException
269+
systemop raiseDefect
266270
registerCallback c, "stdlib.staticos.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
267271
setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
268272
registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =

lib/pure/asyncmacro.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ template createCb(futTyp, strName, identName, futureVarCompletions: untyped) =
4646
{.gcsafe.}:
4747
next.addCallback(cast[proc() {.closure, gcsafe.}](proc =
4848
identName(fut, it)))
49-
except:
49+
except Exception:
5050
futureVarCompletions
5151
if fut.finished:
5252
# Take a look at tasyncexceptions for the bug which this fixes.

lib/pure/unittest.nim

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -556,15 +556,16 @@ template test*(name, body) {.dirty.} =
556556
body
557557
{.pop.}
558558

559-
except:
559+
except Exception:
560560
let e = getCurrentException()
561561
let eTypeDesc = "[" & exceptionTypeName(e) & "]"
562562
checkpoint("Unhandled exception: " & getCurrentExceptionMsg() & " " & eTypeDesc)
563-
if e == nil: # foreign
564-
fail()
565-
else:
566-
var stackTrace {.inject.} = e.getStackTrace()
567-
fail()
563+
var stackTrace {.inject.} = e.getStackTrace()
564+
fail()
565+
566+
except:
567+
checkpoint("Unhandled exception: " & getCurrentExceptionMsg() & " [<foreign exception>]")
568+
fail()
568569

569570
finally:
570571
if testStatusIMPL == TestStatus.FAILED:
@@ -760,6 +761,14 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
760761
expect IOError, OSError, ValueError, AssertionDefect:
761762
defectiveRobot()
762763

764+
template expectException(errorTypes, lineInfoLit, body): NimNode {.dirty.} =
765+
try:
766+
body
767+
checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
768+
fail()
769+
except errorTypes:
770+
discard
771+
763772
template expectBody(errorTypes, lineInfoLit, body): NimNode {.dirty.} =
764773
{.push warning[BareExcept]:off.}
765774
try:
@@ -770,17 +779,23 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
770779
fail()
771780
except errorTypes:
772781
discard
773-
except:
782+
except Exception:
774783
let err = getCurrentException()
775784
checkpoint(lineInfoLit & ": Expect Failed, " & $err.name & " was thrown.")
776785
fail()
777786
{.pop.}
778787

779788
var errorTypes = newNimNode(nnkBracket)
789+
var hasException = false
780790
for exp in exceptions:
791+
if exp.strVal == "Exception":
792+
hasException = true
781793
errorTypes.add(exp)
782794

783-
result = getAst(expectBody(errorTypes, errorTypes.lineInfo, body))
795+
if hasException:
796+
result = getAst(expectException(errorTypes, errorTypes.lineInfo, body))
797+
else:
798+
result = getAst(expectBody(errorTypes, errorTypes.lineInfo, body))
784799

785800
proc disableParamFiltering* =
786801
## disables filtering tests with the command line params

lib/system.nim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2312,8 +2312,16 @@ when notJSnotNims and hostOS != "standalone":
23122312
##
23132313
## .. warning:: Only use this if you know what you are doing.
23142314
currException = exc
2315+
2316+
proc raiseDefect() {.compilerRtl.} =
2317+
let e = getCurrentException()
2318+
if e of Defect:
2319+
reportUnhandledError(e)
2320+
rawQuit(1)
2321+
23152322
elif defined(nimscript):
23162323
proc getCurrentException*(): ref Exception {.compilerRtl.} = discard
2324+
proc raiseDefect*() {.compilerRtl.} = discard
23172325

23182326
when notJSnotNims:
23192327
{.push stackTrace: off, profiler: off.}

lib/system/embedded.nim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring
4242
proc reraiseException() {.compilerRtl.} =
4343
sysFatal(ReraiseDefect, "no exception to reraise")
4444

45+
proc raiseDefect() {.compilerRtl.} =
46+
sysFatal(ReraiseDefect, "exception handling is not available")
47+
4548
proc writeStackTrace() = discard
4649

4750
proc unsetControlCHook() = discard

lib/system/jssys.nim

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ proc raiseException(e: ref Exception, ename: cstring) {.
154154
e.trace = rawWriteStackTrace()
155155
{.emit: "throw `e`;".}
156156

157+
proc raiseDefect() {.compilerproc, asmNoStackFrame.} =
158+
if isNimException():
159+
let e = getCurrentException()
160+
if e of Defect:
161+
if excHandler == 0:
162+
unhandledException(e)
163+
when NimStackTrace:
164+
e.trace = rawWriteStackTrace()
165+
{.emit: "throw `e`;".}
166+
157167
proc reraiseException() {.compilerproc, asmNoStackFrame.} =
158168
if lastJSError == nil:
159169
raise newException(ReraiseDefect, "no exception to reraise")

testament/important_packages.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pkg "asyncthreadpool", "nimble test --mm:refc"
4242
pkg "awk"
4343
pkg "bigints"
4444
pkg "binaryheap", "nim c -r binaryheap.nim"
45-
pkg "BipBuffer"
45+
pkg "BipBuffer", url = "https://github.com/nim-lang/BipBuffer"
4646
pkg "bncurve"
4747
pkg "brainfuck", "nim c -d:release -r tests/compile.nim"
4848
pkg "c2nim", "nim c testsuite/tester.nim"
@@ -66,7 +66,7 @@ pkg "delaunay"
6666
pkg "docopt"
6767
pkg "dotenv"
6868
pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl"
69-
pkg "elvis"
69+
pkg "elvis", url = "https://github.com/nim-lang/elvis"
7070
pkg "eth", "nim c -o:common -r tests/common/all_tests"
7171
pkg "faststreams"
7272
pkg "fidget"

0 commit comments

Comments
 (0)