The following code shows issues with dealing with deep immutability. I have written the type that i think V should be using on each pointer declaration line. One line modifies immutable data - to fix this mut needs to be part of the type, not just an attribute on a variable:
struct S { i int }
fn main() {
p1 := &S{} //type: *S
// p1.i++ // error
mut p2 := p1// type: mut(*)S
s1 := S{}
p2 = &s1// OK, can be reassigned
p2.i++// BUG, s1 is not mutable!
mut s2 := S{}
p2 = &s2// currently allowed, but *p2 was immutable
p3 := &s2// type: *mut(S)
// p3.i++// error, could be allowed as *p3 is mut
mut p4 := &s2
p4.i++// OK
}
(BTW the automatic formatting of comments is not helpful, the first line's comment gets moved to a new line - bug, and space before & after // gets added or removed. There should be a space before comments after code. The programmer may want no space after // when commenting out lines, so they are distinct from real comments).
Pointers need a lot of work, thanks.
I don't use a lot of pointers when developing Volt, that's why they received no love.
The comments thing will be fixed asap.
This also applies to arrays:
fn main() {
a := [2, 3]// type: []int
mut b := a// type: mut([])int
// a[0] = 1 // error
b[0] = 1// BUG
assert a[0] == 2 // fails
mut c := [4, 5] // type: mut[]int
d := c// type:[]mut(int)
// d[0]=1 // error, but could be allowed
}
c above should actually be initialized with [mut 4,5].mut(*)T is more important than having head immutability to mutable tail *mut(T), we can just use mut for both as a workaround. Head mutability is important to change what a pointer is pointing to, whilst making sure that the pointer can't mutate pointed to data that is immutable.Edit: added assert, add type for c.
I'm also seeing this issue.
struct X {
is []int
}
fn main() {
x := X {
is: [1, 2]
}
mut a := x.is
a[0] = 3 // This can modify non-mut field
println(x.is) // [3, 2]
}
In C,
struct X {
int is[2];
};
struct X new_x(int i, int j) {
struct X x;
x.is[0] = i;
x.is[1] = j;
return x;
}
int main()
{
const struct X x = new_x(1, 2);
int *p = x.is;
p[0] = 3;
return 0;
}
does not make a compiler error but warning is raised. And if I'm correct, casting const field to non-const value and modifying it causes undefined behavior.
foo.c:15:10: warning: initializing 'int *' with an expression of type 'int const[2]' discards qualifiers
[-Wincompatible-pointer-types-discards-qualifiers]
int *p = x.is;
^ ~~~~
1 warning generated.
I think V compiler should check immutable field/variable is not assigned to mutable field/variable.
I noticed simpler example for this with 2-dim array.
fn main() {
a := [[1]]
mut b := a[0]
b[0] = 2
println(a[0][0]) // => 2
}
The example provided no longer compiles on V 0.1.24 6d30697 as expected.
a.v:10:5: cannot modify immutable field `i` (type `Sa`)
declare the field with `mut:`
struct Sa {
mut:
i int
}
8| s1 := Sa{}
9| p2 = &s1// OK, can be reassigned
10| p2.i++// BUG, s1 is not mutable!
^
11| mut s2 := Sa{}
12| p2 = &s2// currently allowed, but *p2 was immutable
Most helpful comment
This also applies to arrays:
cabove should actually be initialized with[mut 4,5].mut(*)Tis more important than having head immutability to mutable tail*mut(T), we can just usemutfor both as a workaround. Head mutability is important to change what a pointer is pointing to, whilst making sure that the pointer can't mutate pointed to data that is immutable.Edit: added
assert, add type forc.