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.

Reply via email to