www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Weird rule for immutable

reply Victor Porton <porton narod.ru> writes:
https://dlang.org/spec/const3.html has a weird rule:

"The second way is to cast data to immutable. When doing so, it 
is up to the programmer to ensure that any mutable references to 
the same data are not used to modify the data after the cast."

This rule is weird because it disallows the following code:

int[] x = [];
{
   immutable y = cast(immutable)x;
}
x = [1, 2];

Here x is modified only after the immutable reference goes out of 
scope.

Why this is disallowed? Is it the language designers' intention? 
Why? Or is it bad wording in the specification?

Consider another example, I assume also invalid(?):

void f(ref immutable int v) { }

int x = 1;
f();
x = 2; // the immutable reference does not exist at this point
        // but the assignment is disallowed(?)
Apr 10
next sibling parent reply Victor Porton <porton narod.ru> writes:
On Saturday, 11 April 2020 at 01:19:29 UTC, Victor Porton wrote:
 void f(ref immutable int v) { }

 int x = 1;
 f();
 x = 2; // the immutable reference does not exist at this point
        // but the assignment is disallowed(?)
should be: void f(ref immutable int v) { } int x = 1; f(x); x = 2; // the immutable reference does not exist at this point // but the assignment is disallowed(?)
Apr 10
next sibling parent Victor Porton <porton narod.ru> writes:
On Saturday, 11 April 2020 at 01:24:53 UTC, Victor Porton wrote:
 On Saturday, 11 April 2020 at 01:19:29 UTC, Victor Porton wrote:
 void f(ref immutable int v) { }

 int x = 1;
 f();
 x = 2; // the immutable reference does not exist at this point
        // but the assignment is disallowed(?)
should be: void f(ref immutable int v) { } int x = 1; f(x); x = 2; // the immutable reference does not exist at this point // but the assignment is disallowed(?)
A related issue I've posted: https://issues.dlang.org/show_bug.cgi?id=20728
Apr 10
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Saturday, 11 April 2020 at 01:24:53 UTC, Victor Porton wrote:
 void f(ref immutable int v) { }

 int x = 1;
 f(x);
 x = 2; // the immutable reference does not exist at this point
        // but the assignment is disallowed(?)
You can't pass a mutable int to a `ref immutable int` parameter. (Without 'ref', D will allow passing a mutable int to an immutable int because a copy is made).
Apr 11
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 11 April 2020 at 01:19:29 UTC, Victor Porton wrote:
 Why this is disallowed?
Immutable means it NEVER changes. If you want it to just temporarily not change, that's what const is for.
Apr 10
next sibling parent reply Victor Porton <porton narod.ru> writes:
On Saturday, 11 April 2020 at 01:26:28 UTC, Adam D. Ruppe wrote:
 On Saturday, 11 April 2020 at 01:19:29 UTC, Victor Porton wrote:
 Why this is disallowed?
Immutable means it NEVER changes. If you want it to just temporarily not change, that's what const is for.
Yes, specification reads this way by me, too. But is there any practical reason that the specification disallows the following? int[] x = []; { immutable y = cast(immutable)x; } x = [1, 2];
Apr 10
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 11 April 2020 at 01:45:20 UTC, Victor Porton wrote:
 But is there any practical reason that the specification 
 disallows the following?
Any code that actually uses immutable data is allowed to make various assumptions about it, like keeping the reference for later, aggressively caching, etc. Your code doesn't break that so it works (that's actually allowed by "undefined behavior"; the language doesn't define what it does, but you, the programmer, can know things the spec doesn't), but in general code can do other things with it that would break here.
 int[] x = [];
 {
   immutable y = cast(immutable)x;
 }
 x = [1, 2];
technically btw this code is not strictly wrong because it is the REFERENCE where immutability matters, and here you are replacing it with new data, not actually modifying the old. Just like `string a = "foo"; a = "bar";` is allowed despite string being immutable contents. But I'm pretending it means what you think it means.
Apr 10
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 4/10/2020 6:45 PM, Victor Porton wrote:
 But is there any practical reason that the specification disallows the
following?
 
 int[] x = [];
 {
    immutable y = cast(immutable)x;
 }
 x = [1, 2];
Because Data Flow Analysis would needed to prove there are not other mutations to x within the lifetime of y. DFA is slow and complex, and doesn't seem worth it for this case.
Apr 11
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/10/20 9:45 PM, Victor Porton wrote:
 On Saturday, 11 April 2020 at 01:26:28 UTC, Adam D. Ruppe wrote:
 On Saturday, 11 April 2020 at 01:19:29 UTC, Victor Porton wrote:
 Why this is disallowed?
Immutable means it NEVER changes. If you want it to just temporarily not change, that's what const is for.
Yes, specification reads this way by me, too. But is there any practical reason that the specification disallows the following? int[] x = []; {   immutable y = cast(immutable)x; } x = [1, 2];
So let me modify your example to address Adam's point: int[] x = [1,2]; { immutable y = cast(immutable)x; } x[] = 5; And let's do something else: void foo(const(int)[] z) {writeln(z);} foo(x); Theoretically, the compiler could assume that x's data has become immutable and by logical deduction, write "[1, 2]". It's usually the case that if you invoke undefined behavior, the compiler can do anything it wants under the given assumptions. -Steve
Apr 11
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11.04.20 03:26, Adam D. Ruppe wrote:
 On Saturday, 11 April 2020 at 01:19:29 UTC, Victor Porton wrote:
 Why this is disallowed?
Immutable means it NEVER changes. If you want it to just temporarily not change, that's what const is for.
No. `const` is for data that may or may not change at any time, just not by accessing it through that `const` reference. He is absolutely right that the rule is weird.
Apr 11
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 4/11/2020 7:30 PM, Timon Gehr wrote:
 the rule is weird.
The rule is a consequence of not requiring Data Flow Analysis to diagnose whether a mutable reference outlives it and is used.
Apr 12
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12.04.20 22:39, Walter Bright wrote:
 On 4/11/2020 7:30 PM, Timon Gehr wrote:
 the rule is weird.
The rule is a consequence of not requiring Data Flow Analysis to diagnose whether a mutable reference outlives it and is used.
? The compiler is not even expected to diagnose wrong casting.
Apr 12
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11.04.20 03:19, Victor Porton wrote:
 https://dlang.org/spec/const3.html has a weird rule:
 
 "The second way is to cast data to immutable. When doing so, it is up to 
 the programmer to ensure that any mutable references to the same data 
 are not used to modify the data after the cast."
 
 This rule is weird because it disallows the following code:
 
 int[] x = [];
 {
    immutable y = cast(immutable)x;
 }
 x = [1, 2];
 ...
It does not. You are not mutating any data that has been cast to immutable, you are just rebinding a mutable reference. Maybe you meant to write something like this instead: int[] x = [1, 2]; { immutable y = cast(immutable)x; } x[0] = 3;
 Here x is modified only after the immutable reference goes out of scope.___
 
 Why this is disallowed? Is it the language designers' intention? Why? Or 
 is it bad wording in the specification?
 ...
The wording in the spec is (probably) both intentional and bad.
 Consider another example, I assume also invalid(?):
 
 void f(ref immutable int v) { }
 
 int x = 1;
 f(cast(*immutable(int*)&x);
 x = 2; // the immutable reference does not exist at this point
         // but the assignment is disallowed(?)
According to spec I think it's invalid, therefore it is impossible to manually manage immutable memory. I think the specification should rather be something along the lines that it is undefined behavior to read different values using the same (logical) reference to immutable data.
Apr 11
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12.04.20 04:26, Timon Gehr wrote:
 On 11.04.20 03:19, Victor Porton wrote:
 ...

 int x = 1;
 f(cast(*immutable(int*)&x);
Typos, I meant to write `f(*cast(immutable(int)*)&x)`.
 x = 2; // the immutable reference does not exist at this point
         // but the assignment is disallowed(?)
Apr 11
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 4/11/2020 7:26 PM, Timon Gehr wrote:
 According to spec I think it's invalid, therefore it is impossible to manually 
 manage immutable memory. I think the specification should rather be something 
 along the lines that it is undefined behavior to read different values using
the 
 same (logical) reference to immutable data.
I've made a start on defining lifetimes in the spec, but it's hard to turn "I know what I mean" into legalese. I also wish to carefully avoid crafting semantics that require Data Flow Analysis to implement. It does turn out to be necessary to do DFA for live, but at least that's optional. This is why, for example, the language uses lexical closures to determine where to insert destructors rather than "last use".
Apr 12