www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Error handling meeting summary

reply Richard (Rikki) Andrew Cattermole <richard cattermole.co.nz> writes:
Hello all!

We've had a meeting on how error handling should work in D.

Approved work:

- Finalisers are brought back to the baseline behaviour. They 
will not be rewritten to sequences in the frontend. 
https://github.com/dlang/dmd/blob/3d06a911ac442e9cde5fd5340624339a23af6eb8/compiler/src/dmd/statementsem.d#L3428

- A CLI switch may be offered that performs rewriting of finally 
statements to sequences if an ``Exception`` is not thrown within 
the try body and finally statements will be rewritten to 
``catch(Exception)`` statements. This is Walter's approach to 
error handling that he recommends. It cannot be made the default 
as it will break language features.

- A filter method is added to the ``Thread`` class hierarchy; 
this filter method, by default, will call a grave digger global 
function pointer if set. Otherwise, all ``Throwable``'s will 
propagate like they do today.

     It was considered that perhaps it would be a good idea to a 
flag on ``Thread`` creation to determine if you want to kill the 
process if ``Error`` is seen. No decision is made but I see no 
reason a PR wouldn't be accepted if it only changes the default 
behaviour of the filter method.

I'm happy to do finally statement being brought back to baseline, 
and filter method. However, it will be in the next two months, 
not now. If someone wants to get to it before me they may.

Walter will have to do the CLI switch stuff, due to intersecting 
with dmd's glue code.

Approved work for someone who wants to do it:

- A pragma within a function that disables unwinding. Requires a 
DIP. Requires Iain&Martin's consultation.

- Contracts being called in the caller, not callee. Walter wants 
a DIP document to cover the current state (does not need to go 
into the queue, it only has to exist). Timon offered to do it. 
Anyone can implement as long as we get the document.

- Null deref read barrier aka null check. CLI switch, throw Error 
with a hook function. Noted that this is useful for platforms 
like web assembly and debugging CI's where no stack trace is 
present.

Now for the trouble-making question, is there something that 
people need that I haven't covered in these lists?
Jul 25
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Friday, 25 July 2025 at 16:58:43 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Hello all!

 We've had a meeting on how error handling should work in D.

 Approved work:

 - Finalisers are brought back to the baseline behaviour. They 
 will not be rewritten to sequences in the frontend. 
 https://github.com/dlang/dmd/blob/3d06a911ac442e9cde5fd5340624339a23af6eb8/compiler/src/dmd/statementsem.d#L3428

 - A CLI switch may be offered that performs rewriting of 
 finally statements to sequences if an ``Exception`` is not 
 thrown within the try body and finally statements will be 
 rewritten to ``catch(Exception)`` statements. This is Walter's 
 approach to error handling that he recommends. It cannot be 
 made the default as it will break language features.

 - A filter method is added to the ``Thread`` class hierarchy; 
 this filter method, by default, will call a grave digger global 
 function pointer if set. Otherwise, all ``Throwable``'s will 
 propagate like they do today.

     It was considered that perhaps it would be a good idea to a 
 flag on ``Thread`` creation to determine if you want to kill 
 the process if ``Error`` is seen. No decision is made but I see 
 no reason a PR wouldn't be accepted if it only changes the 
 default behaviour of the filter method.

 I'm happy to do finally statement being brought back to 
 baseline, and filter method. However, it will be in the next 
 two months, not now. If someone wants to get to it before me 
 they may.

 Walter will have to do the CLI switch stuff, due to 
 intersecting with dmd's glue code.

 Approved work for someone who wants to do it:

 - A pragma within a function that disables unwinding. Requires 
 a DIP. Requires Iain&Martin's consultation.

 - Contracts being called in the caller, not callee. Walter 
 wants a DIP document to cover the current state (does not need 
 to go into the queue, it only has to exist). Timon offered to 
 do it. Anyone can implement as long as we get the document.

 - Null deref read barrier aka null check. CLI switch, throw 
 Error with a hook function. Noted that this is useful for 
 platforms like web assembly and debugging CI's where no stack 
 trace is present.

 Now for the trouble-making question, is there something that 
 people need that I haven't covered in these lists?
While adding flags that increase safety, consider adding flags that decrease safety. Maybe replace int/0=>int.max or maybe exceptions are just replaced with `return` and maybe half of phoboes io functions work as is or any of my other hottakes
Jul 25
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Friday, 25 July 2025 at 16:58:43 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Hello all!

 We've had a meeting on how error handling should work in D.

 Approved work:

 - Finalisers are brought back to the baseline behaviour. They 
 will not be rewritten to sequences in the frontend.
Can you explain this? The link isn't very explanatory for those who don't understand the DMD codebase. -Steve
Jul 25
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 26/07/2025 12:03 PM, Steven Schveighoffer wrote:
 On Friday, 25 July 2025 at 16:58:43 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 Hello all!

 We've had a meeting on how error handling should work in D.

 Approved work:

 - Finalisers are brought back to the baseline behaviour. They will not 
 be rewritten to sequences in the frontend.
Can you explain this? The link isn't very explanatory for those who don't understand the DMD codebase. -Steve
Yeah sure. Right now final statements are different behavior based upon if the trybody scope could throw an ``Exception`` class. If they do not throw an ``Exception``, it will rewrite it so that it is a sequence: ``trybody; finalbody;`` Final statements are used for a lot of things, running destructors of structs, or ``scope(exit)`` for instance. That's a problem, its half way between two cleanup strategies. Never run cleanup on throwing of an ``Error`` or always cleaning up when ``Error`` is thrown. An example of this behavior is the following code, where the destructor will run: ```d void do1() { assert(0); } void main() { static struct S { ~this() { import std.stdio; writeln("destructor ran!"); } } S s; do1; } ``` Bringing this back to baseline behavior of no rewrites, means that we can pick which strategy people want to use.
Jul 26
parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Saturday, 26 July 2025 at 19:55:17 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 26/07/2025 12:03 PM, Steven Schveighoffer wrote:
 On Friday, 25 July 2025 at 16:58:43 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 Hello all!

 We've had a meeting on how error handling should work in D.

 Approved work:

 - Finalisers are brought back to the baseline behaviour. They 
 will not be rewritten to sequences in the frontend.
Can you explain this? The link isn't very explanatory for those who don't understand the DMD codebase. -Steve
Yeah sure. [...]
Unfortunately the explanation isn't very clear for me. Am I correct to think that this will ensure structs in `nothrow` functions now get their destructors run on assert / throw Error?
Jul 26
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 27/07/2025 9:12 AM, Sebastiaan Koppe wrote:
 On Saturday, 26 July 2025 at 19:55:17 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 26/07/2025 12:03 PM, Steven Schveighoffer wrote:
 On Friday, 25 July 2025 at 16:58:43 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 Hello all!

 We've had a meeting on how error handling should work in D.

 Approved work:

 - Finalisers are brought back to the baseline behaviour. They will 
 not be rewritten to sequences in the frontend.
Can you explain this? The link isn't very explanatory for those who don't understand the DMD codebase. -Steve
Yeah sure. [...]
Unfortunately the explanation isn't very clear for me. Am I correct to think that this will ensure structs in `nothrow` functions now get their destructors run on assert / throw Error?
Yes, that is correct. Walter's approach where this does not happen will have a switch to enable (assuming he implements it).
Jul 26
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 26 July 2025 at 21:12:02 UTC, Sebastiaan Koppe wrote:
 Unfortunately the explanation isn't very clear for me. Am I 
 correct to think that this will ensure structs in `nothrow` 
 functions now get their destructors run on assert / throw Error?
OpenD implemented it back in May: https://github.com/opendlang/opend/commit/40e11759a82175b2f5edfe855c53a75ce88f176c So you can always run a test case through the opend compiler to see what happens. Try this for example: ``` bool destroyed; struct A { ~this() { destroyed = true; } } void foo() nothrow { assert(0); } void thing() { A a; foo(); } void main() { try { thing(); } catch(Throwable t) { } assert(destroyed); } ``` You'd certainly expect `A`'s dtor to be called at the end of `thing`, but with upstream, that's not actually the case, since it thinks the function doesn't do anything that could throw (`foo` being marked `nothrow` is important to get this result), and thus never bothers setting up the `finally` block internally to call that dtor, it just tries to do it at normal function return. assert (and range error, or null pointer error - also implemented in opend a couple months ago, etc) ignore nothrow though, making it possible to break this assumption. Even if you never *caught* the `Throwable` like I did here, you still might notice the missing side effects of the destructor in other ways like if it did something outside your process.
Jul 26
parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Sunday, 27 July 2025 at 01:05:57 UTC, Adam D. Ruppe wrote:
 On Saturday, 26 July 2025 at 21:12:02 UTC, Sebastiaan Koppe 
 wrote:
 Unfortunately the explanation isn't very clear for me. Am I 
 correct to think that this will ensure structs in `nothrow` 
 functions now get their destructors run on assert / throw 
 Error?
OpenD implemented it back in May:
Yes I saw you mention it in the mile-long thread on errors. Makes a lot of sense. Might have to try out opend one day.
Jul 27