DConf talk : Exceptions will disappear in the future?

sighoya sighoya at gmail.com
Wed Jan 6 17:36:07 UTC 2021


On Tuesday, 5 January 2021 at 21:46:46 UTC, H. S. Teoh wrote:
> 4) The universal error type contains two fields: a type field 
> and a context field.
>
>     a) The type field is an ID unique to every thrown exception 
> --
>     uniqueness can be guaranteed by making this a pointer to 
> some static
>     global object that the compiler implicitly inserts per throw
>     statement, so it will be unique even across shared 
> libraries. The
>     catch block can use this field to determine what the error 
> was, or
>     it can just call some standard function to turn this into a 
> string
>     message, print it and abort.

Why it must be unique? Doesn't it suffice to return the typeid 
here?

>
>     b) The context field contains exception-specific data that 
> gives
>     more information about the nature of the specific instance 
> of the
>     error that occurred, e.g., an integer value, or a pointer 
> to a
>     string description or block of additional information about 
> the
>     error (set by the thrower), or even a pointer to a
>     dynamically-allocated exception object if the user wishes 
> to use
>     traditional polymorphic exceptions.

Okay, but in 99% you need dynamically allocated objects because 
the context is most of the time simply unknown.

But yes, in specific cases a simple error code suffice, but even 
then it would be better to be aware that an error code is 
returned instead of a runtime object. It sucks to me to box over 
the context pointer/value to find out if it is an error code or 
not when I only want an error code.
>
>     c) The universal error type is constrained to have trivial 
> move
>     semantics, i.e., propagating it up the call stack is as 
> simple as
>     blitting the bytes over. (Any object(s) it points to need 
> not be
>     thus constrained, though.)
>
> The value semantics of the universal error type ensures that 
> there is no overhead in propagating it up the call stack.  The 
> universality of the universal error type allows it to represent 
> errors of any kind without needing runtime polymorphism, thus 
> eliminating the overhead the current exception implementation 
> incurs.

So it seems the universal error type just tells me if there is or 
isn't error and checking for it is just a bitflip?

> The context field, however, still allows runtime polymorphism 
> to be supported, should the user wish to.

Which in most of the cases will be required.

> The addition of the universal error type to return value is 
> automated by the compiler, and the user need not worry about 
> it.  The usual try/catch syntax can be built on top of it.
>
> Of course, this was proposed for C++, so a D implementation 
> will probably be somewhat different.  But the underlying thrust 
> is: exceptions become value types by default, thus eliminating 
> most of the overhead associated with the current exception 
> implementation.

I didn't know exactly how this is implemented in D, but class 
objects are passed as simple pointer and pointers are likewise 
value types.
Using value types itself doesn't guarantee anything about 
performance, because the context field of an exception can be 
anything you need some kind of boxing involving runtime 
polymorphism anyway.

>  Stack unwinding is replaced by normal function return 
> mechanisms, which is much more optimizer-friendly.

I heard that all the time, but why is that true?

> This also lets us support exceptions in @nogc code.

Okay, this would be optionally great. However, if we insert the 
context pointer into a List we may get a problem of cyclicity.

> There is no need for a cascade of updates if you do it right. 
> As I hinted at above, this enumeration does not have to be a 
> literal enumeration from 0 to N; the only thing required is 
> that it is unique *within the context of a running program*.  A 
> pointer to a static global suffices to serve such a role: it is 
> guaranteed to be unique in the program's address space, and it 
> fits in a size_t.  The actual value may differ across different 
> executions, but that's not a problem: any references to the ID 
> from user code is resolved by the runtime dynamic linker -- as 
> it already does for pointers to global objects.  This also 
> takes care of any shared libraries or dynamically loaded .so's 
> or DLLs.

What means unique, why is it important? Type ids aren't unique to 
distinguish exceptions and I don't know why we need this 
requirement.
The point in Rust or Java was to limit the plurality of error 
types a function call receive, but this is exactly the point 
where idiomatic and productive development differs. Assumptions 
change and there you are.


> I've said this before, that the complaints about the current 
> exception handling mechanism is really an issue of how it's 
> implemented, rather than the concept of exceptions itself.

Okay, I think this is definitely debatable.

>  If we implement Sutter's proposal, or something similar 
> suitably adapted to D, it would eliminate the runtime overhead, 
> solve the @nogc exceptions issue, and still support traditional 
> polymorphic exception objects that some people still want.

If we don't care of the exception type nor on the kind of message 
of an exception did we have either runtime overhead excluding 
unwinding?
I refer here to the kind of exception as entity. Does a class 
object really require more runtime polymorphism than a tagged 
union?

The other point is how to unify the same frontend (try catch) 
with different backends (nonlocal jumps+unwinding vs value type 
errors implicitly in return types).
You can use Sutter's proposal in your whole project, but what is 
with libraries expecting the other kind of error handling backend.
Did we provide an implicit conversion from one backend to another 
either by turning an error object into an exception or vice versa?



More information about the Digitalmars-d-learn mailing list