Re: Immutability and arrays
On Tuesday, 14 December 2021 at 16:21:03 UTC, Steven Schveighoffer wrote: On 12/14/21 11:19 AM, Steven Schveighoffer wrote: Er... scratch that, this isn't construction, it should use opAssign. Again, probably because memcpy+postblit is used by the runtime. If not reported, it should be. Simple proof that it is a bug: ```d immutable (ComplexStruct)[] arr; ComplexStruct[] arr2; arr2[0] = arr[0]; // ok arr2[] = arr[]; // error ``` If you can copy one element, you should be able to copy all the elements. -Steve Thank you everybody, especially to Steve for the detailed explanation. It seems that every post I put in the learn forum, results in a bug report :) https://issues.dlang.org/show_bug.cgi?id=22601 https://issues.dlang.org/show_bug.cgi?id=22600
Re: Immutability and arrays
On 12/14/21 11:19 AM, Steven Schveighoffer wrote: Er... scratch that, this isn't construction, it should use opAssign. Again, probably because memcpy+postblit is used by the runtime. If not reported, it should be. Simple proof that it is a bug: ```d immutable (ComplexStruct)[] arr; ComplexStruct[] arr2; arr2[0] = arr[0]; // ok arr2[] = arr[]; // error ``` If you can copy one element, you should be able to copy all the elements. -Steve
Re: Immutability and arrays
On 12/14/21 11:14 AM, Steven Schveighoffer wrote: On 12/14/21 10:53 AM, Stanislav Blinov wrote: Now, since we have copy ctors, slice assignment should, ostensibly, attempt to copy-assign elements (i.e. absent opAssign, try the copy ctor first). I agree slice-assign should work here with a copy ctor. What I think is happening is that it's still very much built on memcpy + postblit (so much of the array runtime is still magic functions). postblit doesn't work because it first makes a copy of the data AS-IS, which means it's still immutable, and only after the postblit would it be mutable. Er... scratch that, this isn't construction, it should use opAssign. Again, probably because memcpy+postblit is used by the runtime. If not reported, it should be. -Steve
Re: Immutability and arrays
On 12/14/21 10:53 AM, Stanislav Blinov wrote: Now, since we have copy ctors, slice assignment should, ostensibly, attempt to copy-assign elements (i.e. absent opAssign, try the copy ctor first). I agree slice-assign should work here with a copy ctor. What I think is happening is that it's still very much built on memcpy + postblit (so much of the array runtime is still magic functions). postblit doesn't work because it first makes a copy of the data AS-IS, which means it's still immutable, and only after the postblit would it be mutable. -Steve
Re: Immutability and arrays
On Tuesday, 14 December 2021 at 15:28:30 UTC, Steven Schveighoffer wrote: All the other problems you are having are deriving from this problem. Not exactly. One of the problems seems to be a genuine bug: ```d struct S { int[] x; // doesn't even participate here, neither would postblit this(ref return scope inout S other) { x = other.x.dup; } void opAssign(ref return scope inout S other) { x = other.x.dup; } } void main() { immutable(S)[] src = [S([1, 2]), S([3, 4])]; auto dst = new S[src.length]; //dst[0 .. $] = src[0 .. $]; // this fails to compile even with opAssign defined // this works: foreach (i, ref it; dst) it = src[i]; } ``` Spec: https://dlang.org/spec/arrays.html#array-copying ...contents of the array are the target of the assignment... Per that wording, slice assignment should perform the equivalent of that foreach loop (after overlap checks, etc.). It doesn't, just tries implicit conversion, and fails. Now, since we have copy ctors, slice assignment should, ostensibly, attempt to copy-assign elements (i.e. absent opAssign, try the copy ctor first).
Re: Immutability and arrays
On 12/14/21 3:44 AM, rumbu wrote: I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ``` I know there have been several answers as to what the rules are, I want to answer why the rules are there. In the first case, you have a simple struct which has a single integer in it. When copying that struct, you have no indirections (pointers), which means that adjusting the mutability is allowed: ```d immutable s = SimpleStruct(5); SimpleStruct s2 = s; // ok, we are making a copy of everything ``` In the second case, the `int[]` contains a pointer. So if you made a copy of that, you cannot change the mutability of the type, because now it would have a mutable pointer to immutable data: ```d immutable s = ComplexStruct([5]); ComplexStruct s2 = s; // error, cannot implicitly convert, there is an indirection ``` The WHY is this: let's say the above was allowed, `s2` is mutable, which means `s2.x` is mutable. Now I can do: ```d s2.x[0] = 6; ``` And all of a sudden, immutable data has changed! This cannot be allowed, which is why you can't copy the struct. All the other problems you are having are deriving from this problem. -Steve
Re: Immutability and arrays
On Tuesday, 14 December 2021 at 12:13:23 UTC, Stanislav Blinov wrote: Because is(typeof(immutable(ComplexStruct).x) == immutable(int[])). Can't bind an array of immutable to array of mutable. This would require a deep copy, i.e. copy constructor. This means that the only way to write a generic function which copies an array of immutable elements to another array is this: ```d void copy10(T)(T[] dst, immutable(T)[] src) { static if (is(immutable(T): T)) dst[0..10] = src[0..10]; else dst[0..10] = cast(T[])src[0..10]; // or better a deep copy } ``` Btw, tried to give ComplexStruct a some copy constructors (casting away immutable is just for the example, I know it's not the way to do it). ```d struct ComplexStruct { int[] x; this(ref return scope ComplexStruct another) { this.x = another.x; } this(ref return scope immutable(ComplexStruct) another) { this.x = cast(int[])(another.x); } } ``` Still slice assignment does not work. I think I will drop immutability, it's too complicated to work with.
Re: Immutability and arrays
On Tuesday, 14 December 2021 at 08:44:02 UTC, rumbu wrote: I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ``` Because is(typeof(immutable(ComplexStruct).x) == immutable(int[])). Can't bind an array of immutable to array of mutable. This would require a deep copy, i.e. copy constructor.
Re: Immutability and arrays
On Tuesday, 14 December 2021 at 08:44:02 UTC, rumbu wrote: I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ``` there are special cases in the compiler for values that have no mutable indirections: https://dlang.org/spec/const3.html#implicit_qualifier_conversions Values that have no mutable indirections (including structs that don't contain any field with mutable indirections) can be implicitly converted across mutable, const, immutable, const shared, inout and inout shared. so that first struct may be implicitly converted between mutable/immutable/const because it doesn't contain any mutable indirections (mutable arrays/pointers/references)
Immutability and arrays
I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ```