Jump to page: 1 2
Thread overview
July 20

In response to some concerns around the situation for RAII structs going into closures, I am proposing a resolution to this that will be a language-wide guarantee, not an ultra-specific, not-a-guarantee solution.

https://github.com/dlang/dmd/issues/18704

If a struct destructor is annotated with an attribute @stackonly it may only be called if the this pointer is allocated on the stack. It does not overload.

struct RAII {
    ~this() @stackonly {}
}

void foo() {
    RAII* gc = new RAII; // Error: RAII can only be cleaned up if it is on the stack
    scope RAII* stack1 = new RAII; // ok
    RAII stack2 = RAII(); // ok
}

The attribute is inferred based upon the fields.

struct Wrapper {
    RAII raii;

    ~this() /* @stackonly */ {}
}

As closure creation can see this, no variable that is stack only, cannot be moved into a closure.

Currently D does not model GC vs non-GC pointers, therefore moving into pointers is also disallowed.

RAII* ptr = ...;
*ptr = RAII(); // Error

But only in @safe code. For @system code, it is allowed to by-pass this restriction for data structures.

July 20

On Sunday, 20 July 2025 at 17:04:11 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

In response to some concerns around the situation for RAII structs going into closures, I am proposing a resolution to this that will be a language-wide guarantee, not an ultra-specific, not-a-guarantee solution.

https://github.com/dlang/dmd/issues/18704

If a struct destructor is annotated with an attribute @stackonly it may only be called if the this pointer is allocated on the stack. It does not overload.

struct RAII {
    ~this() @stackonly {}
}

void foo() {
    RAII* gc = new RAII; // Error: RAII can only be cleaned up if it is on the stack
    scope RAII* stack1 = new RAII; // ok
    RAII stack2 = RAII(); // ok
}

The attribute is inferred based upon the fields.

struct Wrapper {
    RAII raii;

    ~this() /* @stackonly */ {}
}

As closure creation can see this, no variable that is stack only, cannot be moved into a closure.

Currently D does not model GC vs non-GC pointers, therefore moving into pointers is also disallowed.

RAII* ptr = ...;
*ptr = RAII(); // Error

But only in @safe code. For @system code, it is allowed to by-pass this restriction for data structures.

wouldnt something in core like bool onStack(void*) be be simpler?

import core.???;
void main(){
  int i;
  void* j=cast(void*)&i;
  assert(onStack(j));
  class bleh{}
  ...
  assert( ! onStack(&blehwhatever));
}
July 21

On Sunday, 20 July 2025 at 17:04:11 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

In response to some concerns around the situation for RAII structs going into closures, I am proposing a resolution to this that will be a language-wide guarantee, not an ultra-specific, not-a-guarantee solution.

https://github.com/dlang/dmd/issues/18704

If a struct destructor is annotated with an attribute @stackonly it may only be called if the this pointer is allocated on the stack. It does not overload.

Isn't the obvious solution to this bug (and related ones like issue #19119) to simply skip the end-of-scope destructor call for structs that are allocated in heap closures? Why introduce this weird, special-purpose hack instead of fixing the root cause of the problem?

July 21
On 21/07/2025 12:00 PM, Paul Backus wrote:
> On Sunday, 20 July 2025 at 17:04:11 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> In response to some concerns around the situation for RAII structs going into closures, I am proposing a resolution to this that will be a language-wide guarantee, not an ultra-specific, not-a-guarantee solution.
>>
>> https://github.com/dlang/dmd/issues/18704
>>
>> If a struct destructor is annotated with an attribute ``@stackonly`` it may only be called if the ``this`` pointer is allocated on the stack. It does not overload.
> 
> Isn't the obvious solution to this bug (and related ones like [issue #19119][1]) to simply skip the end-of-scope destructor call for structs that are allocated in heap closures? Why introduce this weird, special- purpose hack instead of fixing the root cause of the problem?
> 
> [1]: https://github.com/dlang/dmd/issues/19119

Ah see, I am arguing that the GC should handle cleanup.

Problem is, some people believe it shouldn't. Hence this proposal to give those that want that "reliability" control to make it so.

July 21
On Monday, 21 July 2025 at 00:31:10 UTC, Richard (Rikki) Andrew Cattermole wrote:
>
> Ah see, I am arguing that the GC should handle cleanup.

Having read further, it looks like the problem is that the GC currently does not have access to the necessary information to call a closure's destructors at runtime (because D's closures are type-erased). It seems like it ought to be possible to add this information to the closure's context, though, at least in principle.

> Problem is, some people believe it shouldn't. Hence this proposal to give those that want that "reliability" control to make it so.

If there are people who believe that (a) the struct instance should be allocated with the GC, but (b) the GC should not be responsible for its cleanup, then those people are wrong, plain and simple.

On the other hand, if people want to assume control over both the allocation and the cleanup, that's already possible with existing language features.
July 21
On 21/07/2025 2:03 PM, Paul Backus wrote:
>     Problem is, some people believe it shouldn't. Hence this proposal to
>     give those that want that "reliability" control to make it so.
> 
> If there are people who believe that (a) the struct instance should be allocated with the GC, but (b) the GC should not be responsible for its cleanup, then those people are wrong, plain and simple.

Ah no, they don't like the GC introducing unreliability to the cleanup.
So they want to ban destructors in closures.

My position ends up being: you want predictability? Ok, lets solve for that. But the default should align with convenience.

Just disabling destructors doesn't give the predictability that you seek, there has to be more language checks than that.

July 21
On Monday, 21 July 2025 at 02:10:09 UTC, Richard (Rikki) Andrew Cattermole wrote:
> On 21/07/2025 2:03 PM, Paul Backus wrote:
>>     Problem is, some people believe it shouldn't. Hence this proposal to
>>     give those that want that "reliability" control to make it so.
>> 
>> If there are people who believe that (a) the struct instance should be allocated with the GC, but (b) the GC should not be responsible for its cleanup, then those people are wrong, plain and simple.
>
> Ah no, they don't like the GC introducing unreliability to the cleanup.
> So they want to ban destructors in closures.

This seems like a totally self-inflicted problem. If they don't want to rely on the GC for cleanup, nobody is forcing them to. D has plenty of mechanisms for predictable lifetime management already.

> Just disabling destructors doesn't give the predictability that you seek, there has to be more language checks than that.

What "checks" could we possibly introduce that would help with this?
July 22
On 21/07/2025 2:47 PM, Paul Backus wrote:
> On Monday, 21 July 2025 at 02:10:09 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> On 21/07/2025 2:03 PM, Paul Backus wrote:
>>>     Problem is, some people believe it shouldn't. Hence this proposal to
>>>     give those that want that "reliability" control to make it so.
>>>
>>> If there are people who believe that (a) the struct instance should be allocated with the GC, but (b) the GC should not be responsible for its cleanup, then those people are wrong, plain and simple.
>>
>> Ah no, they don't like the GC introducing unreliability to the cleanup.
>> So they want to ban destructors in closures.
> 
> This seems like a totally self-inflicted problem. If they don't want to rely on the GC for cleanup, nobody is forcing them to. D has plenty of mechanisms for predictable lifetime management already.

The problem is closure creation isn't always predictable just from looking at your code.

Add to it, not having @localnogc and we don't have any guarantees to prevent this.

Yes I'm acknowledging that these are either bugs or perceived bugs.

>> Just disabling destructors doesn't give the predictability that you seek, there has to be more language checks than that.
> 
> What "checks" could we possibly introduce that would help with this?

See my original post.

Its because of transitory nature of structs with interaction with no guaranteed cleanup of pointers.

```d
struct Wrapper {
	RAII raii;
}

void func() @safe {
	Wrapper* wrapper = new Wrapper; // Error: Wrapper's destructor is @stackonly due to field raii.
}
```

If you want guaranteed cleanup the only way to make that happen is to get the struct on the stack where the compiler can see it. If you don't do this, you do not have the guarantee to begin with.
July 31
On Monday, 21 July 2025 at 02:10:09 UTC, Richard (Rikki) Andrew Cattermole wrote:
> On 21/07/2025 2:03 PM, Paul Backus wrote:
>>     Problem is, some people believe it shouldn't. Hence this proposal to
>>     give those that want that "reliability" control to make it so.
>> 
>> If there are people who believe that (a) the struct instance should be allocated with the GC, but (b) the GC should not be responsible for its cleanup, then those people are wrong, plain and simple.
>
> Ah no, they don't like the GC introducing unreliability to the cleanup.

"Doctor, doctor, it hurts when I do this".

The GC isn't even guaranteed to run, I don't know what the alternative would be.
August 01
On 31/07/2025 8:42 PM, Atila Neves wrote:
> On Monday, 21 July 2025 at 02:10:09 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> On 21/07/2025 2:03 PM, Paul Backus wrote:
>>>     Problem is, some people believe it shouldn't. Hence this proposal to
>>>     give those that want that "reliability" control to make it so.
>>>
>>> If there are people who believe that (a) the struct instance should be allocated with the GC, but (b) the GC should not be responsible for its cleanup, then those people are wrong, plain and simple.
>>
>> Ah no, they don't like the GC introducing unreliability to the cleanup.
> 
> "Doctor, doctor, it hurts when I do this".
> 
> The GC isn't even guaranteed to run, I don't know what the alternative would be.

The stack.

That is the point, one of the positions on this is to limit such structs from ever leaving the stack, and the point of this proposal is to allow those who want such guarantees to have it.
« First   ‹ Prev
1 2