Deprecating this(this)

Jonathan M Davis newsgroup.d at jmdavisprog.com
Mon Apr 2 17:28:27 UTC 2018


On Monday, April 02, 2018 08:56:41 H. S. Teoh via Digitalmars-d wrote:
> On Sun, Apr 01, 2018 at 02:31:06PM +0000, Nicholas Wilson via Digitalmars-
d wrote:
> > On Sunday, 1 April 2018 at 13:37:43 UTC, Jonathan M Davis wrote:
> > > One issue is that postblit constructors fundamentally don't work
> > > with const. The problem is that a postblit constructor works by
> > > copying the object and _then_ mutating it, and you can't mutate a
> > > const object. To cleanly deal with const, you need something more
> > > like a copy constructor where you initialize it with the adjusted
> > > values directly rather than mutating the copy.
> >
> > I've always wondered about that, is the difference between that
> > anything more than philosophical? Put another way if a this(this) is
> > weakly pure, is there any safety issues with the compiler permitting
> > the mutation on a (non-shared? not sure if this would be a
> > requirement) const object? I'm not sure what the spec says, but if you
> > take the view that the const object is no fully initialised until the
> > postblit is done, then I don't see the problem.
>
> Yeah I've been wondering about this too.  I mean, currently, ctors are
> allowed to assign to immutable fields, because, well, it's
> initialization, not after-the-fact mutation.  Why can't we extend this
> to postblits?  Now, there's certainly the issue of ctors "leaking"
> mutable references to immutable fields if not implemented properly, but
> since postblits are run after a built-into-the-language copying of the
> original, supposedly an opaque process, it seems reasonable enough to
> allow the postblit to be regarded as initialization and able to assign
> to const/immutable fields once.
>
> At the very least, allow rebinding of const/immutable references in the
> postblit. (Allowing straight-out reassignment may have adverse effects
> if the copy shares a reference to an immutable object.)

The core problem is that in a postblit, you're reading a member variable and
then assigning to it, which violates const. For some simple cases, we could
essentially make the member variables tail-const within the postblit,
because we could safely say that the member variable was a distinct copy and
that overwriting it wouldn't really cause problems - e.g. assigning to an
int wouldn't really be a problem, and assigning to const(T)* wouldn't really
be a problem. For classes, it's a bit hinky, because there's no such thing
as tail-const for classes, but they're conceptually the same as const(T)*,
so we could cheat in a postblit and still let the reference be assigned
once. However, the real problem is once you start dealing with structs. Not
only is there no real concept of tail-const with structs, and they can
contain any combination of value types, reference types, pseudo-reference
types, etc. but they can overload opAssign and postblit. And of course, the
postblit constructor for member variables is run before the postblit
constructor for the object containing them, and there's no guarantee that
the postblit actually did _anything_ involving making a deep copy (e.g. it
could simply have been printing out that the object was copied).

So, figuring out how independent a copy the struct is from the original
isn't necessarily very straightforward, and even if it were, how would we do
the equivalent of const(T)* to allow the struct to be reassigned without
mucking with any data that's not independent? It might contain members that
have their data directly embedded in the struct and members which are
reference types, but it's still one unit, and stuff like opAssign is not
designed with the idea that you just overwrite some of the struct. And of
course, once opAssign is overloaded, who knows what the semantics of
assigning to the struct are. As such, how do you reason about the safety of
relaxing the type system to allow assignment to a struct that has overloaded
opAssign?

At one point, Kenji was working on a solution to the problem (I _think_ that
he had a DIP on it, but it's been a while, so I don't remember), and as I
recall, Walter and Andrei vetoed it, because it was too complicated. And I
don't even know if he actually, fully solved the problem.

Ultimately, this is all much, much cleaner if copying an object involves
initializing it directly rather than doing a shallow copy and then doing a
deep copy and reassigning pieces of the object. That sucks for the simple
cases, because if you don't care about stuff like const, and your object has
a bunch of members in it, with a postblit constructor, you might only have
to manually do something with a few of them (whereas in C++, you'd have to
list them all individually), but as far as I can tell, it's pretty much
required for the complex cases.

Off the top of my head, what I would probably do if I were redesigning this
would be to go with copy constructors but improve the syntax so that you
don't have to list any of the member variables unless you're actually going
to be giving them different values. If that could be done cleanly, then it
might end up being a bit more verbose depending on what we had to do, or it
might look pretty much the same as now, except that you'd then be assigning
from a parameter (be it implicit or explicit) representing the object being
copied rather than the object being constructed. Either way, I wouldn't go
with an actual postblit constructor. It's a great idea if const isn't a
thing, but since const is a thing, it's not so great an idea. It also isn't
possible with postblit constructors to look at the original object to get
stuff like its address, which could matter in the uses cases that the DIP
for opMove is trying to solve. So, I'm inclined to think that copy
constructors would ultimately be a better solution - especially if we can
keep the syntax such that you don't have to explicitly initialize any
members that you just want copied. At that point, syntactically, they'd be
pretty much the same as postblit constructors.

- Jonathan M Davis



More information about the Digitalmars-d mailing list