digitalmars.D - should destroy!false be system?
- Steven Schveighoffer (37/37) Feb 23 2023 Let's say you have a struct that manages a malloc'd resource:
- Paul Backus (10/12) Feb 23 2023 It doesn't actually matter if you make `destroy!false` `@system`,
- Steven Schveighoffer (9/17) Feb 23 2023 If we want truly `@safe` code, then we need to define what is expected.
- kinke (8/8) Feb 23 2023 I think it's clear that `destroy!false` should be `@system`. -
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
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
On 2/23/23 12:45 PM, Paul Backus wrote:On Thursday, 23 February 2023 at 17:01:05 UTC, Steven Schveighoffer wrote: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? -Steveis 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.
Feb 23 2023
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