Bug 19122 – Postblits and destructors called on members of anonymous unions
Status
RESOLVED
Resolution
FIXED
Severity
blocker
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-07-27T14:27:22Z
Last change time
2019-06-19T01:13:10Z
Keywords
pull, safe, spec, wrong-code
Assigned to
No Owner
Creator
Simen Kjaeraas
Comments
Comment #0 by simen.kjaras — 2018-07-27T14:27:22Z
@safe unittest {
import std.stdio : writeln;
struct S1 {
int* p;
~this() {
writeln("Writing garbage to random memory: ", p);
//*p = 80085;
}
}
struct S2 {
union {
S1 a;
int b;
}
int index;
this(S1 s) {
a = s;
index = 0;
}
this(int n) {
b = n;
index = 1;
}
}
S2 s = 123456;
}
The above code shows that destructors of union members will be called when the surrounding struct is destructed. In this case, this leads to reinterpretation of an int as a pointer, but worse could happen.
If multiple union members have destructors, all of them are called, regardless of what's currently in the union.
According to https://github.com/dlang/dmd/pull/5830:
"Unrestricted unions can have fields that have destructors, postblits, or invariants. It's up to the user to call them correctly, the compiler does not automatically generate calls to them."
This is clearly not what's happening here. Worse, there's no way to tell the compiler not to call the destructors, short of hiding the actual types behind ubyte[]/void[] and casting.
As a crowning achievement, this happens in @safe.
Comment #1 by simen.kjaras — 2018-08-05T18:49:06Z
*** Issue 19141 has been marked as a duplicate of this issue. ***
Comment #2 by simen.kjaras — 2019-05-28T11:51:46Z
*** Issue 19150 has been marked as a duplicate of this issue. ***
Comment #3 by simen.kjaras — 2019-05-28T12:08:22Z
*** Issue 19903 has been marked as a duplicate of this issue. ***
Comment #4 by simen.kjaras — 2019-05-28T12:08:38Z
As pointed out in issue 19903, from the spec:
"Unions may have fields that have postblits. However, a union itself never has a postblit. Copying a union does not result in postblit calls for any fields. If those calls are desired, they must be inserted explicitly by the programmer."
Also (https://dlang.org/spec/struct.html#struct-destructor):
"Unions may have fields that have destructors. However, a union itself never has a destructor. When a union goes out of scope, destructors for it's fields are not called. If those calls are desired, they must be inserted explicitly by the programmer."
None of these hold for anonymous unions. I expect because their members are placed in the struct definition (that has a destructor), rather than in a union definition (that has no destructor).
Example showing both postblit and destructor being erroneously called:
struct HasDestructor {
~this() {
++d;
}
this(this) {
++p;
}
}
struct S {
union {
int i;
HasDestructor h;
}
}
int d, p;
unittest {
{
S s;
s = s;
assert(p == 1); // Should fail.
}
assert(d > 1); // Should fail.
}
Comment #5 by dlang-bot — 2019-05-28T12:35:07Z
@RazvanN7 created dlang/dmd pull request #9909 "Fix Issue 19122 - Postblits and destructors called on members of anonymous unions" fixing this issue:
- Fix Issue 19122 - Postblits and destructors called on members of anonymous unions
https://github.com/dlang/dmd/pull/9909
Comment #6 by dlang-bot — 2019-06-19T01:13:10Z
dlang/dmd pull request #9909 "Fix Issue 19122 - Postblits and destructors called on members of anonymous unions" was merged into master:
- 874ded4e2adb0656c01d207c175245658ba5ae49 by RazvanN7:
Fix Issue 19122 - Postblits and destructors called on members of anonymous unions
https://github.com/dlang/dmd/pull/9909