Description
It seems to me that the alias checker rules are at least inconsistent and quite possibly buggy around shared boxes and closures. It's possible, however, that I just don't understand what's going on.
This was motivated by the future code I posted in a recent blog post. The code does something very similar to this:
type foo<T> = { mutable f: option<T> };
fn foo<T>(&&f: foo<T>, g: fn(T)) {
alt f.f {
some(v) { g(v); } // X
none { ... }
}
}
However, the alias checker complained at line X
, presumably because the closure g()
might modify the contents of f.f
, thus invalidating the reference v
.
To fix this without introducing a copy of the type T
, brson and I tried using an immutable box to store the value:
fn foo<T>(&&f: foo<T>, g: fn(T)) {
alt f.f {
some(v) { g(*v); } // X
none { ... }
}
}
The thought was that the @T
is immutable, so this should be ok. But of course the alias checker still complained---and rightly so, since (as far as I understand) we are not holding a ref on v
except via f.f
, which is still mutable. So we replaced g(*v)
with g(*(copy v))
, which it accepts. But this seems wrong to me, because although the execution of g
is now safe, there is still no guarantee that v
is valid after g()
executes.
I could do this, for example:
fn foo<T>(&&f: foo<T>, g: fn(T)) {
alt f.f {
some(v) {
g(*(copy v));
#error["v=%?", v]; // but v could be dead here
}
none { ... }
}
}
This code does indeed compile but the ref count seems to always be 1. However, the same code with uniques gives various errors. In any case, it seems to me that if in fact some(v)
is taking a ref on v
, then this code is safe, but the *(copy v)
is unnecessary. On the other hand, if some(v)
is not taking a ref, then this code is dangerous. So something seems wrong. Unless I'm just confused.
Here, by the way, is a complete program that would seem to demonstrate the safety hole, except that the ref counts printed out are 2 and 1 respectively (I'm not sure where the ref comes from in the second print out...):
use std;
import std::dbg::refcount;
type foo<T> = {
mutable f: option<@T>
};
fn upd<T>(&&f: foo<T>, g: fn(T)) {
alt f.f {
some(v) {
#error["f.f=%? v=%? rc=%?", f.f, v, refcount(v)];
g(*(copy v));
#error["f.f=%? v=%? rc=%?", f.f, v, refcount(v)];
}
none { }
}
}
fn main() {
let x = @{mutable f: some(@3u)};
upd(*x) {|v|
x.f = none;
}
}