* --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:
parent
1d66222901
commit
2deb1e354f
9 changed files with 118 additions and 50 deletions
|
|
@ -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 = ""
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) =
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue