Deprecating this(this)

ag0aep6g anonymous at example.com
Wed Apr 4 12:56:57 UTC 2018


On 04/04/2018 12:37 PM, Steven Schveighoffer wrote:
> With structs, we have the possibility of initialization via different 
> mechanisms: constructor, postblit, .init. All of these are supported by 
> the struct member, but currently you can only invoke postblit if you are 
> in a postblit. And only at the beginning. I would like to see more 
> flexibility for copying.
[...]
> For structs, using .init is a valid initialization, so it's completely
> different from classes, where a constructor MUST be invoked. Indeed,
> there is no mechanism to require calling struct member constructors in 
> the owner's ctor.
[...]

To paraphrase your point: We should be able to initialize a member with 
.init or by calling its constructor (over .init). We should not be 
forced to initialize it with its postblit.

Makes sense. But I see one tricky case: Can I also choose to use the 
blitted value of the member without calling its postblit? I'd say that 
can't be allowed.

So the compiler would have to identify that case and reject it. And now 
we're in uncharted territory. As far as I see, this is not something 
that constructors do (struct or class). They happily accept any .init 
value. But postblits can't accept any blitted value. So we can't say: 
"Just do what constructors do." We have to come up with new rules.

But we don't want to come up with new rules, we want to say: "Just do 
what constructors do." So we require the member postblit, and we say 
that the outer postblit operates on the resulting value like a 
constructor operates on .init.

And then we see that it breaks the type system, and that any analog 
behavior of constructors just means that constructors/.init are broken, too.

[...]
>> struct S
>> {
>>      int x;
>>      this(this) immutable
>>      {
>>          x = 42; /* First write. */
[...]
>>      }
>> }
>>
>> struct T
>> {
>>      S s;
>>      this(this) immutable
>>      {
>>          s = S(13); /* Second write. Breaking immutable? */
> 
> Of course this doesn't compile, because s is considered immutable by 
> now.

It doesn't compile, because this(this) is a broken mess currently.

The first write doesn't compile either. Obviously, with a properly 
implemented immutable this(this), you would at least be allowed to write 
once.

With mutable(!) `this(this)`s, both writes are accepted.

> What I was saying is that we can't allow postblit to modify data 
> that has already been postblitted, because of the reason this example is 
> showing.

Still, I'd like to have a more explosive example. The breakage here is 
very localized, and could possibly be defined away somehow. Like: 
"Unique immutable data is considered 'raw' in constructors and postblit 
functions. That means, the data can be mutated. Only when the 
constructor/postblit returns does it become 'cooked' and truly immutable."

[...]
>> ----
[...]
> I don't think you should be able to write to the same field multiple 
> times in an immutable/const constructor. If so, that's a bug.

Are you counting .init as a write, or is a constructor allowed to build 
on that?

----
import std.stdio;
struct S
{
     int x = 1;
     int y = 3;
     this(int dummy) immutable
     {
         writeln(x); /* 1 */
         ++x; /* Breaking immutable? */
         writeln(x); /* 2 */

         ++y; /* Breaking immutable even though y hasn't been printed 
yet? */
         writeln(y); /* 4 */
     }
}
void main()
{
     auto s = immutable S(0);
}
----

I think those increments could be considered breaking immutable. Maybe 
they must be.

If this were to be outlawed, I'd agree a constructors and postblits are 
fundamentally different.


More information about the Digitalmars-d mailing list