* --define:nimQuirky exception handling for Nim; in preparation of a blog post
* make it work with latest system.nim
* make code more readable
* fixes #10702
This commit is contained in:
Andreas Rumpf 2019-02-18 15:23:05 +01:00 • committed by GitHub
commit 2deb1e354f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 118 additions and 50 deletions

View file

@ -168,7 +168,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
let tryStmt = p.nestedTryStmts.pop let tryStmt = p.nestedTryStmts.pop
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop safe points generated by try # Pop safe points generated by try
if not tryStmt.inExcept: if not tryStmt.inExcept and not isDefined(p.config, "nimQuirky"):
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
# Pop this try-stmt of the list of nested trys # Pop this try-stmt of the list of nested trys
@ -383,7 +383,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
blockLeaveActions(p, blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len, howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen) howManyExcepts = p.inExceptBlockLen)
if (p.finallySafePoints.len > 0): if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"):
# If we're in a finally block, and we came here by exception # If we're in a finally block, and we came here by exception
# consume it before we return. # consume it before we return.
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
@ -920,29 +920,38 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
# #
if not isEmptyType(t.typ) and d.k == locNone: if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d) getTemp(p, t.typ, d)
p.module.includeHeader("<setjmp.h>") let quirkyExceptions = isDefined(p.config, "nimQuirky")
if not quirkyExceptions:
p.module.includeHeader("<setjmp.h>")
genLineDir(p, t) genLineDir(p, t)
var safePoint = getTempName(p.module)
discard cgsym(p.module, "Exception") discard cgsym(p.module, "Exception")
linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint) var safePoint: Rope
linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint) if not quirkyExceptions:
if isDefined(p.config, "nimStdSetjmp"): safePoint = getTempName(p.module)
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint)
elif isDefined(p.config, "nimSigSetjmp"): linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint)
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint) if isDefined(p.config, "nimStdSetjmp"):
elif isDefined(p.config, "nimRawSetjmp"): linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint) elif isDefined(p.config, "nimSigSetjmp"):
else: linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint)
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) elif isDefined(p.config, "nimRawSetjmp"):
startBlock(p, "if ($1.status == 0) {$n", [safePoint]) linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint)
else:
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
var length = sonsLen(t) var length = sonsLen(t)
add(p.nestedTryStmts, (t, false)) add(p.nestedTryStmts, (t, false))
expr(p, t.sons[0], d) expr(p, t.sons[0], d)
linefmt(p, cpsStmts, "#popSafePoint();$n") if not quirkyExceptions:
endBlock(p) linefmt(p, cpsStmts, "#popSafePoint();$n")
startBlock(p, "else {$n") endBlock(p)
linefmt(p, cpsStmts, "#popSafePoint();$n") startBlock(p, "else {$n")
genRestoreFrameAfterException(p) linefmt(p, cpsStmts, "#popSafePoint();$n")
genRestoreFrameAfterException(p)
elif 1 < length and t.sons[1].kind == nkExceptBranch:
startBlock(p, "if (#getCurrentException()) {$n")
else:
startBlock(p)
p.nestedTryStmts[^1].inExcept = true p.nestedTryStmts[^1].inExcept = true
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):
@ -953,7 +962,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
# general except section: # general except section:
if i > 1: lineF(p, cpsStmts, "else", []) if i > 1: lineF(p, cpsStmts, "else", [])
startBlock(p) startBlock(p)
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) if not quirkyExceptions:
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
expr(p, t.sons[i].sons[0], d) expr(p, t.sons[i].sons[0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p) endBlock(p)
@ -969,7 +979,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
[genTypeInfo(p.module, t[i][j].typ, t[i][j].info)]) [genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
if i > 1: line(p, cpsStmts, "else ") if i > 1: line(p, cpsStmts, "else ")
startBlock(p, "if ($1) {$n", [orExpr]) startBlock(p, "if ($1) {$n", [orExpr])
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) if not quirkyExceptions:
linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint)
expr(p, t.sons[i].sons[blen-1], d) expr(p, t.sons[i].sons[blen-1], d)
linefmt(p, cpsStmts, "#popCurrentException();$n") linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p) endBlock(p)
@ -980,7 +991,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
p.finallySafePoints.add(safePoint) p.finallySafePoints.add(safePoint)
genSimpleBlock(p, t.sons[i].sons[0]) genSimpleBlock(p, t.sons[i].sons[0])
discard pop(p.finallySafePoints) discard pop(p.finallySafePoints)
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) if not quirkyExceptions:
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
var res = "" var res = ""

View file

@ -224,7 +224,7 @@ type
proc `==`*(a, b: FileIndex): bool {.borrow.} proc `==`*(a, b: FileIndex): bool {.borrow.}
proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = proc raiseRecoverableError*(msg: string) {.noinline.} =
raise newException(ERecoverableError, msg) raise newException(ERecoverableError, msg)
const const

View file

@ -875,7 +875,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
incl(sym.flags, sfSideEffect) incl(sym.flags, sfSideEffect)
of wNoreturn: of wNoreturn:
noVal(c, it) noVal(c, it)
incl(sym.flags, sfNoReturn) # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
if not isDefined(c.config, "nimQuirky"):
incl(sym.flags, sfNoReturn)
if sym.typ[0] != nil: if sym.typ[0] != nil:
localError(c.config, sym.ast[paramsPos][0].info, localError(c.config, sym.ast[paramsPos][0].info,
".noreturn with return type not allowed") ".noreturn with return type not allowed")

View file

@ -239,6 +239,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if c.config.m.errorOutputs == {}: if c.config.m.errorOutputs == {}:
# fail fast: # fail fast:
globalError(c.config, n.info, "type mismatch") globalError(c.config, n.info, "type mismatch")
return
if errors.len == 0: if errors.len == 0:
localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree) localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
return return

View file

@ -1136,7 +1136,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
# its evaluated result here so that we don't execute it once again in the # its evaluated result here so that we don't execute it once again in the
# final pass # final pass
if a[2].kind in nkCallKinds: if a[2].kind in nkCallKinds:
a[2] = newNodeIT(nkType, a[2].info, t) incl a[2].flags, nfSem # bug #10548
if sfExportc in s.flags and s.typ.kind == tyAlias: if sfExportc in s.flags and s.typ.kind == tyAlias:
localError(c.config, name.info, "{.exportc.} not allowed for type aliases") localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
let aa = a.sons[2] let aa = a.sons[2]
@ -1191,24 +1191,27 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
# compute the type's size and check for illegal recursions: # compute the type's size and check for illegal recursions:
if a.sons[1].kind == nkEmpty: if a.sons[1].kind == nkEmpty:
var x = a[2] var x = a[2]
while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0: if x.kind in nkCallKinds and nfSem in x.flags:
x = x.lastSon discard "already semchecked, see line marked with bug #10548"
if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and else:
s.typ.kind notin {tyObject, tyEnum}: while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0:
# type aliases are hard: x = x.lastSon
var t = semTypeNode(c, x, nil) if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and
assert t != nil s.typ.kind notin {tyObject, tyEnum}:
if s.typ != nil and s.typ.kind notin {tyAlias, tySink}: # type aliases are hard:
if t.kind in {tyProc, tyGenericInst} and not t.isMetaType: var t = semTypeNode(c, x, nil)
assignType(s.typ, t) assert t != nil
s.typ.id = t.id if s.typ != nil and s.typ.kind notin {tyAlias, tySink}:
elif t.kind in {tyObject, tyEnum, tyDistinct}: if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
assert s.typ != nil assignType(s.typ, t)
assignType(s.typ, t) s.typ.id = t.id
s.typ.id = t.id # same id elif t.kind in {tyObject, tyEnum, tyDistinct}:
checkConstructedType(c.config, s.info, s.typ) assert s.typ != nil
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: assignType(s.typ, t)
checkForMetaFields(c, s.typ.n) s.typ.id = t.id # same id
checkConstructedType(c.config, s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
checkForMetaFields(c, s.typ.n)
instAllTypeBoundOp(c, n.info) instAllTypeBoundOp(c, n.info)

View file

@ -297,14 +297,14 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
of tyOptAsRef: assert(false, "mapTypeToAstX") of tyOptAsRef: assert(false, "mapTypeToAstX")
proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
result = mapTypeToAstX(cache, t, info, false, true) result = mapTypeToAstX(cache, t, info, inst=false, allowRecursionX=true)
# the "Inst" version includes generic parameters in the resulting type tree # the "Inst" version includes generic parameters in the resulting type tree
# and also tries to look like the corresponding Nim type declaration # and also tries to look like the corresponding Nim type declaration
proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
result = mapTypeToAstX(cache, t, info, true, false) result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=false)
# the "Impl" version includes generic parameters in the resulting type tree # the "Impl" version includes generic parameters in the resulting type tree
# and also tries to look like the corresponding Nim type implementation # and also tries to look like the corresponding Nim type implementation
proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode =
result = mapTypeToAstX(cache, t, info, true, true) result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=true)

View file

@ -3015,6 +3015,9 @@ template newException*(exceptn: typedesc, message: string;
when hostOS == "standalone": when hostOS == "standalone":
include "$projectpath/panicoverride" include "$projectpath/panicoverride"
when not defined(js) and not defined(nimscript):
include "system/ansi_c"
when not declared(sysFatal): when not declared(sysFatal):
{.push profiler: off.} {.push profiler: off.}
when hostOS == "standalone": when hostOS == "standalone":
@ -3024,6 +3027,22 @@ when not declared(sysFatal):
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} = proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} =
rawoutput(message) rawoutput(message)
panic(arg) panic(arg)
elif defined(nimQuirky) and not defined(nimscript):
proc name(t: typedesc): string {.magic: "TypeTrait".}
proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noReturn.} =
var buf = newStringOfCap(200)
add(buf, "Error: unhandled exception: ")
add(buf, message)
add(buf, arg)
add(buf, " [")
add(buf, name exceptn)
add(buf, "]")
cstderr.rawWrite buf
quit 1
proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} =
sysFatal(exceptn, message, "")
else: else:
proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} = proc sysFatal(exceptn: typedesc, message: string) {.inline, noReturn.} =
var e: ref exceptn var e: ref exceptn
@ -3170,7 +3189,6 @@ when not defined(JS): #and not defined(nimscript):
{.pop.} {.pop.}
when not defined(nimscript): when not defined(nimscript):
include "system/ansi_c"
include "system/memory" include "system/memory"
proc zeroMem(p: pointer, size: Natural) = proc zeroMem(p: pointer, size: Natural) =

View file

@ -354,6 +354,8 @@ proc raiseExceptionAux(e: ref Exception) =
raiseCounter.inc # skip zero at overflow raiseCounter.inc # skip zero at overflow
e.raiseId = raiseCounter e.raiseId = raiseCounter
{.emit: "`e`->raise();".} {.emit: "`e`->raise();".}
elif defined(nimQuirky):
pushCurrentException(e)
else: else:
if excHandler != nil: if excHandler != nil:
if not excHandler.hasRaiseAction or excHandler.raiseAction(e): if not excHandler.hasRaiseAction or excHandler.raiseAction(e):

View file

@ -14,10 +14,10 @@ macro checkType(ex: typed; expected: string): untyped =
macro checkProcType(fn: typed): untyped = macro checkProcType(fn: typed): untyped =
let fn_sym = if fn.kind == nnkProcDef: fn[0] else: fn let fn_sym = if fn.kind == nnkProcDef: fn[0] else: fn
echo fn_sym, "; ", fn_sym.typeKind, "; ", fn_sym.getType.repr, "; ", fn_sym.getTypeImpl.repr echo fn_sym, "; ", fn_sym.typeKind, "; ", fn_sym.getType.repr, "; ", fn_sym.getTypeImpl.repr
proc voidProc = echo "hello" proc voidProc = echo "hello"
proc intProc(a: int, b: float): int {.checkProcType.} = 10 proc intProc(a: int, b: float): int {.checkProcType.} = 10
checkType(voidProc(), "void") checkType(voidProc(), "void")
checkType(intProc(10, 20.0), "int") checkType(intProc(10, 20.0), "int")
@ -38,3 +38,33 @@ block:
Club = Blub Club = Blub
static: doAssert(c == 1) static: doAssert(c == 1)
# bug #10702
type
VectorElementType = SomeNumber | bool
Vec*[N : static[int], T: VectorElementType] = object
arr*: array[N, T]
type
Vec4*[T: VectorElementType] = Vec[4,T]
Vec3*[T: VectorElementType] = Vec[3,T]
Vec2*[T: VectorElementType] = Vec[2,T]
template vecGen(U:untyped,V:typed):typed=
## ``U`` suffix
## ``V`` valType
##
type
`Vec2 U`* {.inject.} = Vec2[V]
`Vec3 U`* {.inject.} = Vec3[V]
`Vec4 U`* {.inject.} = Vec4[V]
vecGen(f, float32)
macro foobar(arg: typed): untyped =
let typ = arg.getTypeInst
doAssert typ.getImpl[^1].kind == nnkCall
var x: Vec2f
foobar(x)