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.