Bug 15847 – It is not an error to call opAssign on an uninitialized object
Status
RESOLVED
Resolution
WONTFIX
Severity
major
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2016-03-29T14:16:00Z
Last change time
2016-03-30T13:26:56Z
Assigned to
nobody
Creator
monkeyworks12
Comments
Comment #0 by monkeyworks12 — 2016-03-29T14:16:13Z
struct Test
{
int n;
int opAssign(int val)
{
assert(n == int.init, "n is in an uninitialized state");
return n = val;
}
}
void main()
{
Test t = void;
//opAssign should not be called here as t is uninitialized
t = 0; //BOOM
}
Comment #1 by schuetzm — 2016-03-29T14:55:02Z
Your test cannot work reliably, but...
It is not a bug that opAssign() is called here. Using void initialization doesn't mean "please turn the first assignment into a construction". That wouldn't work in the general case. It means "don't initialize this variable, I'll take care of it myself".
However, what you wrote on Github - that the assignment operator for the out parameter isn't called - is still true. I've filed it here:
https://issues.dlang.org/show_bug.cgi?id=15848
Comment #2 by monkeyworks12 — 2016-03-29T15:34:24Z
'Using void initialization doesn't mean "please turn the first assignment into a construction".'
No, we definitely don't want that. My point is that it is never valid to have opAssign called on a void initialized object that has a custom opAssign. It works for simple value types, but anything with a custom opAssign cannot allow it. Maybe void initialization should be disabled for types with custom opAssign.
Comment #3 by ag0aep6g — 2016-03-29T18:47:32Z
(In reply to monkeyworks12 from comment #2)
> No, we definitely don't want that. My point is that it is never valid to
> have opAssign called on a void initialized object that has a custom
> opAssign. It works for simple value types, but anything with a custom
> opAssign cannot allow it. Maybe void initialization should be disabled for
> types with custom opAssign.
I don't see how void initialization of types with a custom opAssign is bad enough to disallow it. You just have to avoid calling the opAssign during manual initialization, but that's entirely doable.
----
/* ... struct Test as in your code ... */
Test t = void;
t.n = int.init; /* manual initialization without calling opAssign */
t = 0; /* no problem */
----
A generic version (could also use memcpy or similar):
----
Test tinit = Test.init;
* cast(void[Test.sizeof]*) &t = * cast(void[Test.sizeof]*) &tinit;
----
This is similar to how `cast(void*) someClassObject` can do something surprising when it calls a custom opCast. But casts and void initialization are dangerous, unsafe features to be used with much care. And a programmer who uses them is expected to be aware of opCast and opAssign.
Comment #4 by schuetzm — 2016-03-30T11:02:33Z
It simply isn't possible to detect that the variable is void-initialized in the general case. Other languages can do that by way of their design (e.g. Rust, which does extensive data flow analysis, or languages that have a notion of typestate), but D can't do that. At best, the compiler could try to detect trivial cases like in your example. But note that void-initialization is already @system (at least if references are involved), so as ag0aep6g says, the user is responsible for using it correctly.
Comment #5 by monkeyworks12 — 2016-03-30T13:26:56Z
"It simply isn't possible to detect that the variable is void-initialized in the general case."
While it could be done reasonably well with flow control analysis, I agree that it's a lot of work for a small return. Thus it makes more sense to disallow void initialization for types with a custom opAssign.
"But note that void-initialization is already @system..."
Okay, I forgot this point, and it does mitigate my concerns. I will close this bug.