Thread overview
2 days ago

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?

2 days ago

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

2 days ago

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

1 day ago
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.

1 day ago
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?


1 day ago
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).

23 hours ago
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.
13 hours ago
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.