Jump to page: 1 2
Thread overview
Making Errors errors
Jan 28, 2021
Max Haughton
Jan 28, 2021
Adam D. Ruppe
Jan 28, 2021
Max Haughton
Jan 28, 2021
sighoya
Jan 28, 2021
Paul Backus
Jan 28, 2021
sighoya
Jan 28, 2021
H. S. Teoh
Jan 29, 2021
Paul Backus
Jan 29, 2021
sighoya
Jan 29, 2021
Paul Backus
Jan 30, 2021
Timon Gehr
Jan 30, 2021
Timon Gehr
Jan 28, 2021
Timon Gehr
Jan 29, 2021
Andre Pany
Jan 29, 2021
Max Haughton
Jan 29, 2021
Timon Gehr
Jan 29, 2021
Atila Neves
Jan 29, 2021
Jacob Carlborg
January 28, 2021
It has been discussed in a different thread (Making throwing an error an instant failure, catching Error etc.)

I am starting to put together a patch to try out this behaviour, what do we actually want it to do - should it call a user specified handler, druntime, c etc.?

The rationale makes perfect sense (Errors should indicate something has gone wrong, the program is in an invalid state - by definition you cannot recover), but the exact behaviour must be specified.
January 28, 2021
On Thursday, 28 January 2021 at 17:59:41 UTC, Max Haughton wrote:
> I am starting to put together a patch to try out this behaviour, what do we actually want it to do - should it call a user specified handler, druntime, c etc.?

Here's a crazy idea: when this switch is in place, it just never actually honors your catch handlers.

so then throw Error is the same as an uncaught exception and thus trigger the debugger at the throw site instantly.

> (Errors should indicate something has gone wrong, the
> program is in an invalid state)

In real life Errors are rarely this extreme. Most of them indicate that the program *would* get into an invalid state if it proceeded, but since it stopped before actually executing it, things are still fine.


January 28, 2021
On Thursday, 28 January 2021 at 18:33:17 UTC, Adam D. Ruppe wrote:
> On Thursday, 28 January 2021 at 17:59:41 UTC, Max Haughton wrote:
>> I am starting to put together a patch to try out this behaviour, what do we actually want it to do - should it call a user specified handler, druntime, c etc.?
>
> Here's a crazy idea: when this switch is in place, it just never actually honors your catch handlers.
>
> so then throw Error is the same as an uncaught exception and thus trigger the debugger at the throw site instantly.
>
>> (Errors should indicate something has gone wrong, the
>> program is in an invalid state)
>
> In real life Errors are rarely this extreme. Most of them indicate that the program *would* get into an invalid state if it proceeded, but since it stopped before actually executing it, things are still fine.

Not a bad idea. I think I'll try that first.

And that may well be true but when I use A
assert, for example, I often think of it in the same context as one would in formal verification i.e if this is wrong something catastrophic has happened. If the problem is exceptional but ultimately recoverable use Exceptions.

I also feel that, potentially against the grain of Phobos, parsers should not throw - e.g. std.conv probably fails more than it succeeds in many code bases.

January 28, 2021
On 28.01.21 18:59, Max Haughton wrote:
> It has been discussed in a different thread (Making throwing an error an instant failure, catching Error etc.)
> 
> I am starting to put together a patch to try out this behaviour, what do we actually want it to do - should it call a user specified handler, 

Ideally, yes. The handler should not be allowed to return and it would be useful if there was some way to allow stack unwinding in code compiled using a special compiler flag. (This requires some support from the compiler, basically the backend has to ignore nothrow in such builds.)

> druntime,

I guess that's the same as the first option, just a bit harder to access.

> c etc.?

I don't think the behavior should be hard-coded into the compiler.

> 
> The rationale makes perfect sense (Errors should indicate something has gone wrong, the program is in an invalid state - by definition you cannot recover),

You may be able to recover if the failure is contained in a nonessential component. Process separation is sometimes a good way to do that, but it may be overkill for some projects.

> but the exact behaviour must be specified.

If the program were truly in an invalid state the behavior would be undefined and immediate termination would by definition be impossible to ensure. That's not always useful though, there's plenty of circumstances where immediately killing the program is not what you want, e.g. if the program was recording a sequence of user inputs that will allow you to reproduce the error.

I think the immediate elevation of every bug into an issue that completely undermines the state of the abstract machine is not right for every use case. Those are at different levels of abstraction. Your abstract machine state is still okay even if some access fails a bounds check and if it happens in a nothrow function, there is no way to resume execution after the handler.
January 28, 2021
On Thursday, 28 January 2021 at 18:47:27 UTC, Max Haughton wrote:
> I often think of it in the same context as one would in formal verification i.e if this is wrong something catastrophic has happened.

Catastrophic in which sense? To the whole program?

As mentioned by Adam, logical errors rarely justify whole program abortion. Downstream code doesn't necessarily require computations to succeed on the whole line if it can deal with partial results or provide alternate ways to retrieve missing information.
> If the problem is exceptional but ultimately recoverable use Exceptions.

Not all Exceptions are recoverable especially if we don't know how to deal with. But there may be exemptions silently ignore any kind of exception.
The distinction between error and exception is sometimes hard to resolve. Is it a usual error if the document doesn't parse or is it exceptional?

I think it all depends on subjective assumptions.

> I also feel that, potentially against the grain of Phobos, parsers should not throw - e.g. std.conv probably fails more than it succeeds in many code bases.

This example seems reiterated infinite times and seems true in most cases as a parser error is just a value to deal with, we don't abort the program immediately, we collect errors in order to display them as a sequence of mistakes.

And what to do for cases where aren't interested in any partial results, seldom, but such cases would rather be implemented with exception handling.


January 28, 2021
On Thursday, 28 January 2021 at 19:39:41 UTC, sighoya wrote:
> On Thursday, 28 January 2021 at 18:47:27 UTC, Max Haughton wrote:
>> I often think of it in the same context as one would in formal verification i.e if this is wrong something catastrophic has happened.
>
> Catastrophic in which sense? To the whole program?
>
> As mentioned by Adam, logical errors rarely justify whole program abortion. Downstream code doesn't necessarily require computations to succeed on the whole line if it can deal with partial results or provide alternate ways to retrieve missing information.

From now on, whenever this topic comes up, I'm just going to post this link:

http://joeduffyblog.com/2016/02/07/the-error-model/#bugs-arent-recoverable-errors
January 28, 2021
On Thursday, 28 January 2021 at 19:42:54 UTC, Paul Backus wrote:
> From now on, whenever this topic comes up, I'm just going to post this link:
>
> http://joeduffyblog.com/2016/02/07/the-error-model/#bugs-arent-recoverable-errors

I haven't consumed the whole page, but the examples of both sections (bugs vs recoverable errors) seems to overlap to some extent which justifies the statements I made.

It is sometimes very hard to distinguish between bugs and recoverable errors (which mostly aren't recoverable anyway, i.e. the term is confusing).

A bug is even misleading here because it doesn't relate to error handling only, the majority of bugs doesn't cause errors just silently change the expected result.

A bug is just the mismatch of specification and implementation. It makes sense to model them in language with formal verification requiring the compiler to reject your program if the specification doesn't match the implementation.

Given range indexing as an example, we would certainly think of index violations as bugs. That may be true. However, the code following after the random access wouldn't be executed anyway and the caller may or may not depend hard on the result of the code fragment.

A better take to go would definitely exclude pessimistically out-of-range-errors and bubble up possible errors on a higher level of understanding but that's even hard to do with (path) dependent typing.

The cost is simply too high to support and more overly to adapt such fine-grained modeling.

And introducing errors/bugs in addition to exceptions bifurcates your world again in two parts. I know that errors exist in Java and probably in C# too, but they never really surfaced in productive development or just no one cares about them as their classification as errors is generally debatable.
January 28, 2021
On Thu, Jan 28, 2021 at 11:41:44PM +0000, sighoya via Digitalmars-d wrote:
> On Thursday, 28 January 2021 at 19:42:54 UTC, Paul Backus wrote:
> > From now on, whenever this topic comes up, I'm just going to post this link:
> > 
> > http://joeduffyblog.com/2016/02/07/the-error-model/#bugs-arent-recoverable-errors
[...]
> Given range indexing as an example, we would certainly think of index violations as bugs. That may be true. However, the code following after the random access wouldn't be executed anyway and the caller may or may not depend hard on the result of the code fragment.
[...]

Actually, in the absence of a range check (in C, or D with -release), code following the access *will* be executed, with undefined behaviour. Also known by the more familiar name "buffer overflow exploit".


T

-- 
Why did the mathematician reinvent the square wheel?  Because he wanted to drive smoothly over an inverted catenary road.
January 29, 2021
On Thursday, 28 January 2021 at 23:41:44 UTC, sighoya wrote:
> Given range indexing as an example, we would certainly think of index violations as bugs. That may be true. However, the code following after the random access wouldn't be executed anyway and the caller may or may not depend hard on the result of the code fragment.

If out-of-bounds array access is defined by the language spec as an unrecoverable error, an optimizing compiler is allowed to assume that no program ever recovers from it, and potentially re-order code based on that assumption. So you cannot actually be sure that "the code following after the random access wouldn't be executed."

Of course, the key phrase here is "defined by the language spec." As a language designer, you are free to define out-of-bounds indexing as either recoverable or unrecoverable. But as a programmer, once the decision has been made and the spec has been written, you do not get a choice--either you play by the rules, or your code is wrong.

I suspect a lot of the confusion on this issue comes from people mixing up the programmer's perspective and the language designer's perspective.
January 29, 2021
On Thursday, 28 January 2021 at 17:59:41 UTC, Max Haughton wrote:
> It has been discussed in a different thread (Making throwing an error an instant failure, catching Error etc.)
>
> I am starting to put together a patch to try out this behaviour, what do we actually want it to do - should it call a user specified handler, druntime, c etc.?
>
> The rationale makes perfect sense (Errors should indicate something has gone wrong, the program is in an invalid state - by definition you cannot recover), but the exact behaviour must be specified.

What would be the effect of this change on the unit test runners we have (d-unit, silly, unit-threaded)?
This might break their functionality, as they might catch Errors (unit tests calling assert).

Kind regards
Andre
« First   ‹ Prev
1 2