www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - immutable postblit unusable?

reply Cauterite <cauterite gmail.com> writes:
https://dpaste.dzfl.pl/80d2a208519c

struct A {
	this(this) immutable {}
}

struct B {
	A a;
}

// Error: immutable method f18.A.__postblit is not callable using 
a mutable object

-------------------------

I honestly don't know what to make of this.
I guess the auto-generated postblit of B is throwing this?

Does it even make sense to have an immutable postblit?
Should such code be explicitly rejected?
Feb 10 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, February 11, 2018 07:50:52 Cauterite via Digitalmars-d wrote:
 https://dpaste.dzfl.pl/80d2a208519c

 struct A {
   this(this) immutable {}
 }

 struct B {
   A a;
 }

 // Error: immutable method f18.A.__postblit is not callable using
 a mutable object

 -------------------------

 I honestly don't know what to make of this.
 I guess the auto-generated postblit of B is throwing this?

 Does it even make sense to have an immutable postblit?
 Should such code be explicitly rejected?
Basically, as soon as you have a postblit constructor, the type cannot be const or immutable. Unfortunately, as nice as the idea for postblit constructors is in comparison to copy constructors, it fundamentally doesn't work if the members aren't mutable (since for the postblit constructor to work, you have to mutate the result). Occasionally, there has been talk of adding copy constructors to solve the problem with const, but it's never happened. In the case of immutable, the postblit is actually completely unnecessary, because there's no need to do a deep copy of an immutable object, since it's guaranteed to never change its value (whereas with const, a deep copy can still be necessary, since other references to that data may be mutable and could thus change the data). Now, as to what you've done. You've marked the postblit constructor as immutable. That means that it's only callable when the object is immutable, and A isn't immutable, so B's implicit postblit constructor can't work properly. And declaring an explicit postblit constructor doesn't help. The reason for that is that any type with an explicit postblit constructor really has two posblit constructors. This code struct A { } pragma(msg, __traits(allMembers, A)); struct B { this(this) {} } pragma(msg, __traits(allMembers, B)); struct C { B c; } pragma(msg, __traits(allMembers, C)); prints this tuple() tuple("__postblit", "__xpostblit", "opAssign") tuple("b", "__xpostblit", "opAssign") A doesn't have an explicit postblit constructor, and it doesn't have any member variables with a postblit constructor. So, it has no postblit constructors. On the other hand, B has an explicit postblit constructor. That's the __postblit in the members, and __xpostblit is the implicit postblit constructor. C then has no explicit postblit constructor, so it has no __postblit member, but because it has a member variable with a postblit constructor, it has a __xpostblit member. As I understand it, __xpostblit calls the postblit constructors for each member that needs it and then calls __postblit. So, __xpostblit is essentially the actual postblit constructor that deals correctly with the entire type and all its members recursively, and __postblit is the explicitly declared one that just deals with the type itself. And with what you've done, you've declared __postblit to be immutable, but that has no effect on __xpostblit. So, you get a compilation error about mutable objects not working with __postblit, since __xpostblit only operates on mutable objects. Interestingly, changing your example so that A a; is immutable doesn't actually help, and it could be argued that that's a bug, but it really doesn't matter. If you're dealing with immutable, there's zero reason to have a postblit constructor such that it arguably should be an error to declare a postblit constructor as immutable, and the nature of a postblit constructor fundamentally only works with mutable objects, so arguably, it should be illegal to mark a postblit constructor with either const or immutable. But if you just give up on the postblit constructor, immutable will work just fine. It's const that's screwed, and unless there's a language improvement (most likely involving some kind of copy constructor), const and objects with postblit constructors just fundamentally aren't going to work together. - Jonathan M Davis
Feb 11 2018
next sibling parent reply Cauterite <cauterite gmail.com> writes:
On Sunday, 11 February 2018 at 08:25:41 UTC, Jonathan M Davis 
wrote:
Thanks, I couldn't have asked for a more thorough explanation. Especially that __xpostblit detail, which I now vaguely recall seeing in the runtime code. Sounds like making `this(this) immutable` illegal is the way to go. I don't have anything to add about the `const` problem though.
Feb 11 2018
parent Cauterite <cauterite gmail.com> writes:
issue opened here:
https://issues.dlang.org/show_bug.cgi?id=18417

thanks Jon
Feb 11 2018
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/11/18 3:25 AM, Jonathan M Davis wrote:
 In the case of immutable, the postblit is actually completely unnecessary,
 because there's no need to do a deep copy of an immutable object, since it's
 guaranteed to never change its value (whereas with const, a deep copy can
 still be necessary, since other references to that data may be mutable and
 could thus change the data).
What about reference counting? Essentially, you could be changing values external to the type itself. I don't think postblit should be illegal on immutable types. -Steve
Feb 13 2018