V: pointers and arrays can violate immutability

Created on 22 Mar 2019  路  6Comments  路  Source: vlang/v

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).

Bug Compiler

Most helpful comment

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
}
  • V needs to allow the reference variable to have separate mutability from the array data. So c above should actually be initialized with [mut 4,5].
  • Note: To simplify the type system, having head mutability with immutable tail 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.

All 6 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
}
  • V needs to allow the reference variable to have separate mutability from the array data. So c above should actually be initialized with [mut 4,5].
  • Note: To simplify the type system, having head mutability with immutable tail 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
Was this page helpful?
0 / 5 - 0 ratings

Related issues

shouji-kazuo picture shouji-kazuo  路  3Comments

radare picture radare  路  3Comments

aurora picture aurora  路  3Comments

oleg-kachan picture oleg-kachan  路  3Comments

PavelVozenilek picture PavelVozenilek  路  3Comments