Skip to content

Commit c235cf2

Browse files
committed
Use a MIR dataflow analysis pass to prevent moves instead of basing it on lifetime of borrows.
1 parent 9a8240e commit c235cf2

File tree

1 file changed

+2
-42
lines changed

1 file changed

+2
-42
lines changed

text/0000-immovable-types.md

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,49 +30,9 @@ You can freely move values which are known to implement `Move` after they are bo
3030

3131
Static variables allow types which do not implement `Move`.
3232

33-
## Borrowing immovable types
33+
## Move checking
3434

35-
Borrowing values of types which do not implement `Move` is only allowed if the borrows lasts for the entire lifetime of the values, including the drop of the value, since `drop` takes a reference to it. Reborrows of such borrows follow existing rules.
36-
37-
This means that the following borrow would not be allowed:
38-
```rust
39-
let mutex = StaticMutex::new(0);
40-
{
41-
*mutex.lock() += 1;
42-
}
43-
let moved = mutex;
44-
*moved.lock() += 1;
45-
```
46-
Here `lock` borrows the `mutex` variable. The borrow only last until the end of the same statement. That means we'd be allowed to move `mutex` into a new variable `moved` and call `lock` on it, this time with an different address for `&self`!
47-
48-
We rely on the fact that borrows prevent moves. We cannot change the lifetime of the borrow to encompass the moving statement using the current borrow checker. This can be changed once we get non-lexical lifetime and we'd get an error on the move instead.
49-
50-
Conceptually we can think of borrows of `?Move` values as introducing 2 borrows:
51-
- one borrow with as short lifetime as possible with normal restrictions
52-
- one borrow which must match the lifetime of the borrowed value. The only restriction placed is that the value must not be moved out of.
53-
54-
This RFC suggests an approach where we use only the shorter borrow and require it to match the lifetime of the value. This is less flexible, but results in minimal implementation changes. A more flexible solution can be introduced with non-lexical lifetimes.
55-
56-
We can easily work around issues with this in code by using a single borrow of the immovable value and just reborrow.
57-
58-
Illegal:
59-
```rust
60-
let mutex = StaticMutex::new(0);
61-
*mutex.lock() += 1;
62-
*mutex.lock() += 1;
63-
```
64-
Workaround using reborrows:
65-
```rust
66-
let mutex = &StaticMutex::new(0);
67-
*mutex.lock() += 1;
68-
*mutex.lock() += 1;
69-
```
70-
71-
A borrow such as `&var.field` where `field` is immovable will last as long as the lifetime of `var` to ensure it matches the lifetime of the field.
72-
73-
We need to prevent assignment to immovable types once they have been borrowed. This is because assignment actually moves the l-value before calling `Drop::drop`. If there are any restrictions on the l-value or if the l-value has a dereference operation, assignment to immovable types is not allowed.
74-
75-
Types which implement `Copy`, but not `Move` are allowed. You can still copy them around, but borrows follows the restrictions of `?Move` types.
35+
To prevent values which may not implement `Move` that have been previously borrowed we introduce a MIR pass. We do a forward dataflow analysis on the MIR marking l-values that have been borrowed. For `a` we mark the path `a` as observed. For `a.b` we mark both `a.b` and the parent `a`, since if you observe `a.b` moving `a` will change the address of `a.b`. For `*a` we do nothing, as the address of `a` isn't observed. For slice indices `a[i]`, we mark `a` as observed. Finally we walk through every move in the MIR and give an error if the moved value could be observed at that point and the type of the value isn't known to implement `Move`.
7636

7737
## Immovable types contained in movable types
7838

0 commit comments

Comments
 (0)