www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - should destroy!false be system?

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Let's say you have a struct that manages a malloc'd resource:

```d
struct S(T)
{
    private T* val;
     disable this(this);
    this(T val) {
       this.val = cast(T*)malloc(T.sizeof);
       *this.val = val;
    }
    ~this() {
       free(val);
    }
    ref T get() { return *val; }
}
```

Now, if you just use an S!T on the stack, it works as expected.

But what happens if you destroy `s`? If you do, then it will call the 
destructor a *second time* when the struct goes out of scope. However, 
`destroy` will reinitialize the value with the `.init` value.

What about `destroy!false`? This *doesn't* initialize the value back to 
an .init state, and so leaves a dangling pointer in the above type.

This is a fuzzy area for me. I tend to *always* nullify any resource as 
soon as I destroy it, this way I don't accidentally screw it up later 
(old habit). But is that the correct expectation for a struct that has 
been destroyed? Is it reasonable to expect that once a struct is 
destroyed, it can never be used again (even to call the destructor)?

My thought is that, for a struct with a destructor:

a) the destructor should *always* be callable on its `.init` value.
b) a struct that has been destroyed where its lifetime lives on should 
be overwritten with its '.init' value.
c) if destroyed without overwriting with `.init` value, the program 
should make sure a second destructor call should not be allowed to 
happen in  safe code.

is c) reasonable? Do we need to deprecate  safe calls to `destroy!false` 
for types that have destructors?

-Steve
Feb 23 2023
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 23 February 2023 at 17:01:05 UTC, Steven 
Schveighoffer wrote:
 is c) reasonable? Do we need to deprecate  safe calls to 
 `destroy!false` for types that have destructors?
It doesn't actually matter if you make `destroy!false` ` system`, because the user can still call `s.__dtor` directly--and you can't make the destructor itself ` system` if you want `S` to be usable in ` safe` code. The only way out of this, without significant language changes, is to make the destructor itself idempotent. This is what `SafeRefCounted` does: https://github.com/dlang/phobos/blob/v2.102.1/std/typecons.d#L6953-L6954
Feb 23 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/23/23 12:45 PM, Paul Backus wrote:
 On Thursday, 23 February 2023 at 17:01:05 UTC, Steven Schveighoffer wrote:
 is c) reasonable? Do we need to deprecate  safe calls to 
 `destroy!false` for types that have destructors?
It doesn't actually matter if you make `destroy!false` ` system`, because the user can still call `s.__dtor` directly--and you can't make the destructor itself ` system` if you want `S` to be usable in ` safe` code.
If we want truly ` safe` code, then we need to define what is expected. Should a destructor be expected to be called more than once on a struct that is "live", i.e. not the `.init` value? If so, you have to defensively mark the struct as being "destroyed", if not, you can expect that the language has either done it for you, or will not call your destructor more than once. So which is it? -Steve
Feb 23 2023
parent kinke <noone nowhere.com> writes:
I think it's clear that `destroy!false` should be ` system`. - 
Wrt. `__xdtor` (the *full* dtor, destructing the fields too, so 
might be ` system` even if the user dtor itself is ` safe), that 
would probably be ideally disallowed by the compiler as well, and 
ctors probably too (as they usually operate on the assumption 
that the payload is initialized to its `.init` value). But that's 
IMO way more exotic, and anything with two leading underscores in 
an ` safe` scope quite suspicious.
Feb 23 2023