On 4/1/18 10:59 AM, Nicholas Wilson wrote:
On Sunday, 1 April 2018 at 14:31:24 UTC, Andrei Alexandrescu wrote:
There's a mix of fundamental flaws and bugs. I'll get to the flaws in
a second. About the bugs: people have altered their code in various
ways to work with the bizarre semantics of this(this). Now, if we fix
various bugs in this(this) by virtually redefining it, then we'll
break a lot of code in a lot of ways. To wit, we fixed a small issue
and it already created problems:
https://github.com/dlang/dmd/pull/8032. That didn't contribute to the
decision but is quite illustrative.
I found two fundamental flaws with this(this):
1. For immutable objects, typechecking in the presence of successive
modifications of data (first assignment by the compiler, then
modification by the user) is very difficult if not impossible. I don't
know how to do it. The single initialization model (raw/cooked) used
currently in regular immutable constructors works reasonably well and
is robust.
2. For shared objects, the part done by the compiler and the part done
by this(this) should be synchronized together. This makes it
impossible for the user to e.g. define a struct that gets copied
atomically.
See my other reply: but why is it necessary to consider the blit
logically distinct from the postblit w.r.t to program flow observability?
for 1. consider
immutable foo = ...;
immutable bar = foo;
to be
immutable foo = ...;
immutable bar = () {mutable _ = bitcopy(foo); _.__postblit(); return _;}();
Negative. The problem is typechecking postblit itself, not its invocation.
for 2. you would have to synchronize anyway for shared, it makes no
difference.
Negative. Consider:
shared struct Point
{
private long x, y, z;
private Mutex mutex;
...
}
Task: define the copy primitive of Point so atomically copy x, y, and z
using mutex. The problem is, the compiler will memcpy the three longs
non-atomically before the user even gets a crack at intercepting the
operation.
There'd be an additional issue - this(this) is non-templated, which
requires combinatorial additions when qualifiers are present on the
source or destination side.
(Perhaps this is what you're referring to, but all you have said so far
is "this doesn't work and we need to fix it") the post blit is surely
like a destructor: there's only one way to do it, irrespective of the
attributes, especially of the intermediate is considered mutable until
the end of post blit, like static module constructors initialising
global immutables.
Negative. Ignoring qualifiers during copying opens holes in the type
system the size of China. Or at least Australia as it were :o). Consider:
int[] sneaky;
struct A
{
private int[] innocent;
this(this)
{
sneaky = innocent;
}
}
void main()
{
immutable a = A([1, 2, 3]);
auto b = a;
sneaky[1] = 42; // oops
import std.stdio;
writeln(a.innocent); // ooooops
}
Sadly this (and many similar ones) compiles and runs warning-free on
today's compiler. We really need to close this loop, like, five years ago.
I agree that we should fix any type checking bugs that may be present,
and that we should strive to not make the same mistake twice, however I
wouldn't call either of the above cases a showstopper. You will need to
show more of what is broken and why it is broken given the expected
breakage.
Such discussions will be indeed present in the DIP.
Andrei