You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Furthermore setjmp is an expensive call because it has to store a stack snapshot and this cost is always paid, whether an exception was raised or not.
but on the flip side, what's not mentioned here is that the go-style error handling incurs an extra if branch evaluation for every function in call stack between the raising function and the catching function, which is not the case for setjmp; so which one is more costly depends on factors such as how deeply nested are exceptions, how frequently they throw, and size of snapshot
Other solutions are conceivable too, including a novel static analyis that detects a rule like "the result of procs that can raise must not be written to a heap location"
that's not sufficient, it should also check the proc has no side effect (eg (recursively) calling printf or other C function); and even that's not sufficient, eg with re-entrant code; eg consider this code:
procensure(a: bool) =ifnot a: raisenewException("err")
procfactorial(n: int) : int=ensure(a>=0)
if n ==0: 1else: fact(n-1) * n
try: factorial(readLine().toInt)
except: echo"invalid input"
and call it with echo -1 | ./main => will give stack overflow with quirky exceptions, even though there aren't side effects (beside setting currentException which I'm assuming you're not treating as side-effect, otherwise any proc that can raise will have by definition a side-effect, making this distinction useless)
In more complex situations it maybe be much harder to detect. I don't even know if it's even tractable in general.
Try the araq-quirky-exceptions branch of Nim, compile your code with --define:nimQuirky and try it for yourself.
overall I'd be quite nervous with a codebase using quirky exceptions. Unless static analysis can guarantee 100% no change in semantics by skipping the implicit returnOnError by identifying any potential side effect (or stack overflow, see above example), I'd need to audit entire codebase to make sure each function call is safe to continue on error.
alternative
I'd be curious about the more traditional (go-style except it'd be implicit) approach code transformation that handles exceptions by inserting if (unlikely(currentException != nullptr)) goto catchBlock; after every proc that can raise, and how it compares performance wise to setjmp approach; at least (in theory) there should be no semantic difference, unlike the quirky exceptions approach.
In more complex situations it maybe be much harder to detect. I don't even know if it's even tractable in general.
Maybe read it again, I know about these problems, they don't seem "intractable" at all to me.
overall I'd be quite nervous with a codebase using quirky exceptions. Unless static analysis can guarantee 100% no change in semantics by skipping the implicit returnOnError by identifying any potential side effect (or stack overflow, see above example), I'd need to audit entire codebase to make sure each function call is safe to continue on error.
Maybe read it again then. I said "we can port the stdlib to support this mode, if there is enough interest".
And also: I ported a big complex system in one hour and yet you give me some toy fac as a "counterexample" as if I don't understand what I'm writing about.
this is not accurate AFAIK, there are well defined cases where it's legal to do so since c++11, with
noexcept(false)
see spec here https://en.cppreference.com/w/cpp/language/destructorand for more on this, see https://akrzemi1.wordpress.com/2011/09/21/destructors-that-throw/ ; there are valid use cases.
but on the flip side, what's not mentioned here is that the go-style error handling incurs an extra
if
branch evaluation for every function in call stack between the raising function and the catching function, which is not the case forsetjmp
; so which one is more costly depends on factors such as how deeply nested are exceptions, how frequently they throw, and size of snapshotthat's not sufficient, it should also check the proc has no side effect (eg (recursively) calling printf or other C function); and even that's not sufficient, eg with re-entrant code; eg consider this code:
and call it with
echo -1 | ./main
=> will give stack overflow with quirky exceptions, even though there aren't side effects (beside settingcurrentException
which I'm assuming you're not treating as side-effect, otherwise any proc that can raise will have by definition a side-effect, making this distinction useless)In more complex situations it maybe be much harder to detect. I don't even know if it's even tractable in general.
overall I'd be quite nervous with a codebase using quirky exceptions. Unless static analysis can guarantee 100% no change in semantics by skipping the implicit
returnOnError
by identifying any potential side effect (or stack overflow, see above example), I'd need to audit entire codebase to make sure each function call is safe to continue on error.alternative
I'd be curious about the more traditional (go-style except it'd be implicit) approach code transformation that handles exceptions by inserting
if (unlikely(currentException != nullptr)) goto catchBlock;
after every proc that can raise, and how it compares performance wise tosetjmp
approach; at least (in theory) there should be no semantic difference, unlike the quirky exceptions approach.links
The text was updated successfully, but these errors were encountered: