digitalmars.D - Disagreeing addresses of `this`
- Longinus (37/37) May 12 So my understanding is that structs have an implicit `opAssign`
- Nick Treleaven (18/42) May 12 Not sure if it helps, but...
- Longinus (13/14) May 13 Yeah this is the last time anything is done with `p`. Its
- Max Samukha (6/14) May 13 But the destructor does get called on each initialized object.
So my understanding is that structs have an implicit `opAssign` which takes its argument by value, and that it only calls the destructor on its own copy, but not that of the argument, which may be a temporary. For example: ```d struct S { this(int x) { writeln("ctor ", &this); } ~this() { writeln("dtor ", &this); } } void main() { S s; writeln("main ", &s); s = S(42); } ``` the result, when compiled with DMD, is ``` main 7FFF28396290 ctor 7FFF28396291 dtor 7FFF28396268 dtor 7FFF28396290 ``` There are three different addresses and the ctors and dtors involved in the copy and `opAssign` disagree with each other. Right now this prevents me from using `&this` in ctors & dtors since, as far as temporaries are concerned, this is as if a dtor call is being elided by the compiler. There is this bug report about it: https://issues.dlang.org/show_bug.cgi?id=9666 but I'm not sure of whether the differing addresses are part of what's intended. Are they? Also are there workarounds to get the ctors and dtors to see the same thing, other than compiling with LDC?
May 12
On Sunday, 12 May 2024 at 13:03:35 UTC, Longinus wrote:So my understanding is that structs have an implicit `opAssign` which takes its argument by value, and that it only calls the destructor on its own copy, but not that of the argument, which may be a temporary.Not sure if it helps, but... This is the low-level code (`dmd -vcg-ast`): ```d ref system S opAssign(S p) return { (S __swap40 = void;) , __swap40 = this , (this = p , __swap40.~this()); return this; } ```For example: ```d struct S { this(int x) { writeln("ctor ", &this); } ~this() { writeln("dtor ", &this); } } void main() { S s; writeln("main ", &s); s = S(42); } ``` the result, when compiled with DMD, is ``` main 7FFF28396290`main.s`ctor 7FFF28396291Temporary `S(42)` is constructed on main's stack, passed as argument `p` to `opAssign`. Then: * `main.s` is bit-copied to `__swap40`. * `p` is then bit-copied to `main.s`.dtor 7FFF28396268`__swap40.~this()` inside `opAssign`.dtor 7FFF28396290`main.s.~this()` at the end of `main`.
May 12
On Sunday, 12 May 2024 at 21:09:33 UTC, Nick Treleaven wrote:* `p` is then bit-copied to `main.s`.Yeah this is the last time anything is done with `p`. Its destructor does not get called. I understand that avoiding this further copy may be an optimisation, but while I can have a dtor deal with an uninitialised object (by doing no-op), an initialised object not having its dtor called, that I don't know how to work with. My current work-around is to simply _not_ use temporaries, but it's awkward and I don't know how to make the compiler warn on their usage. (For reference: yes, I'm literally having the ctor/dtor write `&this` in places; so this is introducing dangling pointers) Thanks for looking into it.
May 13
On Tuesday, 14 May 2024 at 02:01:10 UTC, Longinus wrote:On Sunday, 12 May 2024 at 21:09:33 UTC, Nick Treleaven wrote:But the destructor does get called on each initialized object. `s` is moved (not copied) into the uninitialized `__swap40` and then the destructor is called on `__swap40`. Then `p` is moved into `s` and the destructor is called on `s` at `main`'s exit. Note that D mandates all struct objects be movable.* `p` is then bit-copied to `main.s`.Yeah this is the last time anything is done with `p`. Its destructor does not get called. I understand that avoiding this further copy may be an optimisation, but while I can have a dtor deal with an uninitialised object (by doing no-op), an initialised object not having its dtor called, that I don't know how to work with.
May 13