January 09, 2020
On Thursday, January 9, 2020 8:25:17 PM MST Timon Gehr via Digitalmars-d wrote:
> On 10.01.20 02:28, Walter Bright wrote:
> > On 1/9/2020 2:10 PM, Timon Gehr wrote:
> >> On 09.01.20 11:32, Jonathan M Davis wrote:
> >>> I wouldn't mind us getting rid of Error in favor of killing the
> >>> program on
> >>> the spot, since that's actually better for debugging,
> >>
> >> It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
> >
> > Hooking the kill function is the way to do that - much better than exception unwinding.
>
> Exception unwinding gives you a stack trace.

A core dump at the point of the error gives you that and more.

- Jonathan M Davis



January 09, 2020
On 1/9/2020 7:25 PM, Timon Gehr wrote:
> Exception unwinding gives you a stack trace.

Stack trace printing can be independent of the exception unwinding system. And it doesn't require all the complex tracking of RAII objects and their destructors, nor does it require executing code in the upstack functions.
January 09, 2020
On 1/9/2020 7:42 PM, Gregor Mückl wrote:
> What exactly is the execution overhead for non-throwing code paths?
> 
> I believe I understand the overhead once stack unwinding needs to be performed, but how is code generation affected for the normal path?

You need a mechanism for jumping to the scope guard code. Can't do any code motion optimizations across the boundaries of such code. Can't do register assignments for variables that are "live" across that boundary.

Just write some code with destructors in C++, compile it with and without exceptions enabled, and dump the generated assembler.
January 10, 2020
On Friday, 10 January 2020 at 01:28:35 UTC, Walter Bright wrote:
> On 1/9/2020 2:10 PM, Timon Gehr wrote:
>> On 09.01.20 11:32, Jonathan M Davis wrote:
>>> I wouldn't mind us getting rid of Error in favor of killing the program on
>>> the spot, since that's actually better for debugging,
>> 
>> It really depends. There are situations where it may be better, but if you are trying to debug a non-deterministic condition that happens very rarely on someone else's machine exclusively in release builds, depending on a lot of user input, druntime shutting down the code that is supposed to collect enough information for you to have a chance to figure out what is going on in detail can hardly be described as "better". It should be possible to hook the kill function at least.
>
> Hooking the kill function is the way to do that - much better than exception unwinding.

Sounds like a good idea, looking forward to that DIP.

I would consider `nothrow` by default, like others, as seriously crippling to the language.
However improving the efficiency, or changing the scheme, sounds like it would achieve everyone's goal.

To give an example of a real world issue we have:
- A server handling connections
- We call `formattedWrite` with a `nothrow` sink and `nothrow` arguments (`toString` & co)
- `formattedWrite` is not `nothrow`

Why ? Because `std.format` is *full* of `enforce` calls to check the format string.
So what should we do with them ? We *could* consider it user error (because the programmer didn't provide the proper format string) and just bulk-replace it with `Error`.

But now, something as trivial as a wrong format string will crash my server, and for an error which is most likely recoverable (which is I guess the original reasoning behind the `enforce` calls).
And such error are common, and can be in paths that are almost never triggered (e.g. an error message when the filesystem is full).

So all things considered, having a non-`nothrow` `std.format` seems like the best compromise until someone comes up with a smart way to solve this without another downside. The same reasoning apply to many, many places, and that's why `nothrow` by default wouldn't make sense to me.

Note at the moment, you can turn a non-`nothrow` function into a `nothrow` function with a single line of code:
```
void myFunc() nothrow
{
    scope (failure) assert(0); // This makes the function `nothrow`
    formattedWrite(...);
}
```
So in D, you can have non-`nothrow` function and a backtrace via exceptions, or `nothrow` function and a backtrace (abort in release mode). I think that we have it pretty good.

P.S: https://issues.dlang.org/show_bug.cgi?id=20487
January 10, 2020
On Friday, 10 January 2020 at 01:40:00 UTC, Walter Bright wrote:
> On 1/9/2020 12:39 PM, H. S. Teoh wrote:
>> It's not really the exceptions themselves
>> that people object to, but the associated implementation issues.
>
> Yes. They're very costly, even in code that never throws.
>
> An approach I've been using with modest success is to design errors entirely out of the code. For example, in dmd a lot of errors are handled by making a special AST node for "Error". Subsequent code does nothing with Error nodes.
>
> (Analogously to how floating point code deals with errors, it just sets a NaN value, which is sticky.)
>
> Another technique is to check for errors in the data first, then the processing code does not have to check, and cannot fail.
>
> I enjoy trying to set up an API so it cannot fail, then no special code is needed for errors. Of course, this isn't always possible, and isn't a general solution. But it's nice when one can make it work.
>
> P.S. I hate throwing constructors, and would force them to be nothrow in D if I weren't faced with a tsunami of objection to it :-)

Well said. Another option would be functional programming, with an explicit either type. But it is not for everyone.

I have found that I don't need exceptions a lot, and when I do, it is almost always because I want to take advantage of the fact that they add implicit returns up call stack until the nearest catch. Then they come in real handy, especially because they are transparent to the intermediate functions.

Of course, that is exactly why they are costly.

They works great with IO, but I tend to avoid them for everything else.
January 10, 2020
On Friday, 10 January 2020 at 06:19:01 UTC, Jonathan M Davis wrote:
> A core dump at the point of the error gives you that and more.
>
> - Jonathan M Davis

If only recovering core dumps was reliable these days! It has become fashion to misconfigure systems to forward core dumps to systemd-journald where they are promptly discarded because the daemon considers the dumps to be too large for hiding them in the binary syslog.

Apart from that, a crash handler that somehow logs or displays a backtrace is preferable when a crash happened on a user machine. Depending on the nature of the application, a core dump might contain sensitive information that cannot be legally shared with the developer (e.g. an in-memory copy of customer addresses).
January 10, 2020
On Friday, 10 January 2020 at 03:42:53 UTC, Gregor Mückl wrote:
> The longer this discussion drags on, the more I doubt my understanding of this topic.
>
> What exactly is the execution overhead for non-throwing code paths?


My understanding is that there is no overhead for functions that transitively don't throw or catch.


It's also my experience that

    A. _the bottlnecksin a program are almost NEVER functions that throw_ so why optimize the other parts?

    B. Giving up a little bit of safety for performance ruins all potential gains because of the overhead of introducing more bugs. Fixing one such bug will negate whatever performance gain.

    C. If you do need performance AND errors (typically: parsing without ranges/exceptions vs parsing with error codes) then you can resort locally to error codes.

    D. Error codes makes very easy to forget them, misuse them, and makes code less readable. They make very easy to conflate unrecoverable errors and recoverable errors.


The current internet backlash against exceptions is really puzzling to me: it's a sort of _revisionism_ because in the trenches exceptions are holding things together so they don't break more horribly. It think these people don't know how worse it could be.
January 10, 2020
On Friday, 10 January 2020 at 07:06:31 UTC, Walter Bright wrote:
> On 1/9/2020 7:42 PM, Gregor Mückl wrote:
>> What exactly is the execution overhead for non-throwing code paths?
>> 
>> I believe I understand the overhead once stack unwinding needs to be performed, but how is code generation affected for the normal path?
>
> You need a mechanism for jumping to the scope guard code. Can't do any code motion optimizations across the boundaries of such code. Can't do register assignments for variables that are "live" across that boundary.
>
> Just write some code with destructors in C++, compile it with and without exceptions enabled, and dump the generated assembler.

I played around with this as you suggested. The most concise example that I could find where -fno-exceptions makes a difference is this:

https://godbolt.org/z/WxnsOm

The version with exceptions reserves a register for something at the start of the function (rbp in this case), but I can't tell what it's for. It is used in the unwinding handler that is appended to the function after the ret instruction.
January 10, 2020
On Sunday, 5 January 2020 at 13:51:33 UTC, Ola Fosheim Grøstad wrote:
>
> Anyway, C++ deprecated the the "throw" specifier in C++11 and removed it  completely in C++20. Not sure why D users will be more accepting of having to specify "throw".
>
> More patient user base perhaps.

C++ remove exception specification but kept `noexcept` and `throw()` both of which behave like the desired `throw` discussed here for D.


January 10, 2020
On Friday, 10 January 2020 at 15:43:35 UTC, Jesse Phillips wrote:
> C++ remove exception specification but kept `noexcept` and `throw()` both of which behave like the desired `throw` discussed here for D.

No, "throw()" is not only deprecated, it is removed in C++20.