[Issue 5207] Immutability is broken in constructors

d-bugmail at puremagic.com d-bugmail at puremagic.com
Mon Mar 22 16:43:18 UTC 2021


https://issues.dlang.org/show_bug.cgi?id=5207

--- Comment #7 from hsteoh at quickfur.ath.cx ---
Here's an example of a problematic case:

----------
import std;

struct S {
        int x;
}

immutable S s;
immutable(S)* ptr;

void fun(immutable(S)* p) {
        ptr = p;
}

shared static this() {
        fun(&s);

        writeln(ptr.x); // prints 0

        s.x = 1;
        writeln(ptr.x); // prints 1

        s.x = 2;
        writeln(ptr.x); // prints 2: immutability broken
}
----------

Since `ptr` points to an immutable value, one expects that `ptr.x` should not
change. Correspondingly, an optimizing compiler reserves the right to elide
subsequent loads, thus causing different output.

And indeed, if the above code is refactored as follows:

----------
import std;

struct S {
        int x;
}

immutable S s;
immutable S* ptr;

shared static this() {
        ptr = &s;

        writeln(ptr.x); // prints 0

        s.x = 1;
        writeln(ptr.x); // prints 1

        s.x = 2;
        writeln(ptr.x); // prints 2: immutability broken
}

void main() {
        writeln(ptr.x);
}
----------

Then the output becomes:

----------
0
0
0
2
----------

Clearly, the compiler has optimized away the loads of `ptr.x` because of the
immutability of `ptr`, but actually that was a wrong assumption because the
supposedly immutable value *did* change, as proven by what main() sees
afterwards. This causes inconsistent behaviour in either case, the root cause
of which is an immutable value being assignable multiple times inside a ctor.

Immutable values should not be readable before initialization (the first
writeln should be illegal), and should only be initializable once.

--


More information about the Digitalmars-d-bugs mailing list