-
Notifications
You must be signed in to change notification settings - Fork 23
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
Introduce safety modes #180
Comments
Big -1 from me, we don't need more compiler and runtime modes, we need fewer. And let's also not conflate everything together, most experimental language features are about syntactic sugar and have no security impact. |
I'm not sure what kind of safety concerns you want to address but where safety is critical (aerospace, train scheduling systems, ...) what is required is completely different:
See the guarantees levels Spark Pro offer: https://www.adacore.com/sparkpro For me the biggest safety feature that we could bring is a spec extension (maybe similar to Crystal's) and have that compile to a formal verification language like Spin or TLA+ (tuto) and being able to check Nim code against the spec. Or we forge the spec extension and directly compile a restricted subset of Nim/stdlib to those languages. This goes way beyond runtime checks or a borrow checker. This is similar to having a blueprint in civil or mechanical engineering, plan and verify the soundness of the design and then starting to build. This is not a replacement to runtime checks or Memory/Thread/AddressSanitizer by the way. As an example, for Weave I was hunting several deadlocks and livelocks on my backoff mechanism (to put idle threads to sleep) and after spending a weekend dabbling into TLA+ I implemented my protocol, solved the logical and concurrency bugs (one did not exist with 2 threads, and was too rare with 8+ threads). Furthermore it allowed me to unravel a bug in glibc and musl condition variables where signaling them could be ignored, deadlocking the application. See mratsim/weave#56. While I don't expect Nim to be run on planes or space shuttles anytime soon:
Side-notes
Other references:
|
At least that's addressed by |
I'm working on |
|
Does this mean we'll get an effect to track panics as well? I mean, so that we can statically prove that no panics happen in a piece of code? |
Seems needed to implement araqs's sfalwaysreturn in his PR... |
* allow defects to be caught even for --exceptions:goto (WIP) * implemented the new --panics:on|off switch; refs nim-lang/RFCs#180 * new implementation for integer overflow checking * produce a warning if a user-defined exception type inherits from Exception directly * applied Timothee's suggestions; improved the documentation and replace the term 'checked runtime check' by 'panic' * fixes #13627 * don't inherit from Exception directly
Probably yes, but I have no final design for it. |
proposalthese already have a meaning and should keep their meaning: raises: []. # cannot raise a CatchableError (but could raise a Defect)
raises: [OsError]. # can only raise a OsError amongst the Catchable ones (but could raise a Defect) so we introduce a new pragma: exceptions: []. # cannot raise any Exception (so can't raise any Defect either)
exceptions: [OsError]. # can only raise a OsError (and no Defect)
exceptions: [AssertionError]. # can only raise a AssertionError (and no other Defect, unless subclass of AssertionError of course)
exceptions: [AssertionError, IndexError]. # obvious meaning
exceptions: [Defect]. # obvious meaning
exceptions: [Exception]. # obvious meaning |
@timotheecour: after the PR by @Araq , there now seem to be 4 categories of errors to consider: Since the new proposed implementation depends on a compiler flag it means that if you're using a library that was written for one setting of the flag, and you're using |
Why not simply |
no, it's simpler than what you describe;
i thought about that too, but exceptions: [] # same as your defects: [] raises:[]
exceptions: [IndexError] # same as your defects: [IndexError] raises:[]
exceptions: [OsError] # same as your defects: [] raises:[OsError]
raises: [OsError] # same as your raises:[OsError] # this could raise any defect, as well as OsError
raises: [] # same as your raises:[] # this could raise any defect, but no Catchable noteeverything is (obviously) expressible in terms of
so raises is only for convenience of not having to specify |
That's not true. (We could make it true though.) There is |
ya, having a separate |
this is very confusing in practice - the same constructs (try/catch and raises) being used for both "severities" of errors is difficult to educate around - it's "kind of" the same thing at that point - just another error, and deciding between the two when designing a library becomes very subjective - there's no clear boundary. the consequence of having a global compiler flag for it is that you get completely different effects propagated through the code base making it impossible to mix panic:on and panic:off code - for example, it would be very hard to annotate the standard library itself with correct "raises" for both modes - this is a very real problem: almost everywhere where resources are managed in the standard library, there are leaks and errors because the code is not exception safe - these annotations should really be an explicit part of the standard library, because if you raise a new exception suddenly, that's a breaking API change. |
but effect system tracks this so it only affects fwd decls |
Why would you do that though... Just leave it turned off, isn't that what happened already? I introduced turning them on via --exceptions:goto (--gc:arc implies --exceptions:goto) and got so much backslash that I had to revert it. |
IMO:
But other than that your application should usually not rely on Defect's being catchable as it should indicate a bug. |
Here is the problem: Let's assume that a |
If we want to be able to have guarantees for panic-free (defect-free) code, then the current tracking of It's true that there are "educational" problems when it comes to the distinction between errors and defects, but I believe these problems will be present with any of the alternative proposals. Nim has few inappropriate defaults that exacerbate the problem:
prof foo() {.raises: [Defect, MyRecoverableError, OtherRecoverableError]`.} Instead, you end up with procs that must be annotated with
The two compilation modes ( |
There is now a warning for (1) in Nim devel. Will do (2)-(3) for the upcoming release. Edit: 2-3 are still to be done after 1.2. |
Whenever one says |
That's what Java did, the most important problem is that when your library uses callbacks (in Java's case: interfaces), you as the library author do not know if the callback can raise something or not. |
why not "generic" effects? function a raises exactly when arg callback |
The callback handler can also be inside an object, have fun with the complexities in the type system. ;-) |
There are seldom cases where it makes sense for a library to use exceptions instead of using errors or result/either.
For most other cases (IO, streams, networking, serialization, ...), it makes more sense to use errors at the low-level and let/force consumer applications (not libraries) deal with the error. This makes the error model part of the API (the raises: [MyNewException] is an alternative but in practice no one pushed it far enough to show it worked), this makes it easier to provide libraries to consume from other language, this avoids the mistake of Python of using exceptions for all control flow and only coding/testing for the happy path (i.e. all files are found, network connection is never lost, and there is always disk space). |
@Araq well .. you can annotate that if you really want :P :) |
Actually we have a variation of this rule in the effect tracking. It helps a little but also was complex to implement and I doubt many people understand it. ;-) |
Judging from the responses, I'm not sure my point about proc types was understood well, so I'll clarify. Consider a library that uses a function pointer internally. For example, it may provide a user-hook or a callback for a certain operation. The library authors are likely to write their code like this: type
ClickHandler = proc (e: MouseEvent): bool
proc onClick(node: GuiElement, handler: ClickHandler) =
node.clickHandler = handler What happens next is that If the language states that unannotated procs can raise only defects, a whole lot of usages of callbacks will stop compiling and the users will have to think about either handling the exceptions or providing a correct annotation for the callback. |
... or just make exceptions a strict requirement of the function signature - this is already kind of possible (but there are a few language features missing to make this truly convenient): like that, Being able to reason about the deduced exceptions is pretty useful in generic code, as evidenced by the callback problem happens when type erasure is involved, which is when the callback registration is dynamic - but there are plenty of cases where that's not the case. |
...though one has to wonder why no modern language tries to follow java's footsteps and actually put specific exceptions in signatures through |
That's a programming language design fashion, ymmv. In my opinion garbage collection and exception handling are what enabled Python to grow its impressive set of libraries in the first place. And are responsible for Python's success. And yes, also its dynamic typing helped as you can emulate in your custom class what is required by the function
That doesn't match my experience at all. Exceptions are useful when errors, including IO errors, are pervasive. |
A lot of of mission critical software is written in Python and injecting faults during functional testing is common practice. One of the main benefit of exception is constraining failure modes: turning both known and unexpected failures from a system failure (e.g. a process crashing out) into loss of functionality of a subsystem. |
This RFC has been implemented, closing. For defect tracking or other new features / warnings /etc we can create new RFCs. |
For some classes of applications safety, security and the ability to recover from exceptions and bugs are important. Nim could implement a safety option that:
--checks:on
--assertions:on
--lineTrace:on
(and therefore stackTrace)safety:3
, refuse to compile procs that use unsafe operations and inline asm (unless specifically tagged as safe?)when defined(safety): ...
)Related:
The text was updated successfully, but these errors were encountered: