Skip to content

Commit 99f2046

Browse files
committed
Make Move a built in trait again and otherwise align the with PR
1 parent 1783835 commit 99f2046

File tree

1 file changed

+61
-25
lines changed

1 file changed

+61
-25
lines changed

text/0000-immovable-types.md

+61-25
Original file line numberDiff line numberDiff line change
@@ -20,47 +20,84 @@ Since generators can only move during suspend points we can require that referen
2020
# Detailed design
2121
[design]: #detailed-design
2222

23-
A new unsafe auto trait `Move` is introduced in `core::marker`. Auto traits are implemented for all primitive types and for composite types where the elements also implement the trait. Users can opt-out to this for custom types. References, pointers, `core::ptr::Unique` and `core::ptr::Shared` explicitly implement this trait, since pointers are movable even if they point to immovable types.
24-
25-
All type parameters (including `Self` for traits), trait objects and associated types have a `Move` bound by default.
23+
A new builtin marker trait `Move` is introduced in `core::marker`. All type parameters (including `Self` for traits) and associated types implement `Move` by default.
2624

2725
If you want to allow types which may not implement `Move`, you would use the `?Move` trait bound which means that the type may or may not implement `Move`.
2826

27+
A new marker struct `Immovable` is also introduced in `core::marker`. This struct does not implement `Move` and allows users to make composite immovable types.
28+
2929
You can freely move values which are known to implement `Move` after they are borrowed, however you cannot move types which aren't known to implement `Move` after they have been borrowed. Once we borrow an immovable type, we'd know its address and code should be able to rely on the address not changing. This is sound since the only way to observe the address of a value is to borrow it. Before the first borrow nothing can observe the address and the value can be moved around.
3030

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

33+
These are the rules to determine if a type implements `Move`:
34+
- Integers, floats, `char` and `bool` are `Move`
35+
- Function types and function pointers are `Move`
36+
- Closures are `Move` if their captured variables are `Move`
37+
- Movable generators are `Move` if all values (including capture variables) which are live during a suspension point are `Move`
38+
- Imovable generators are never `Move`
39+
- The `Immovable` type is never `Move`
40+
- Trait objects are `Move` if they have an explicit `Move` bound
41+
- Struct, enums and tuples are `Move` if all their elements are `Move`
42+
- Existential types (`impl Trait`) are `Move` if their underlying type are `Move`
43+
- `[T]` and `[T; n]` are `Move` if `T` is `Move`
44+
- `str` is `Move`
45+
3346
## Move checking
3447

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`.
48+
We need to ensure that values we have borrowed no longer can be moved. When we borrow a value we can find its address in memory. For example:
49+
```rust
50+
struct Foo {
51+
field: bool,
52+
}
3653

37-
## Immovable types contained in movable types
54+
fn address(v: &Foo) -> usize {
55+
v as *const _ as usize
56+
}
3857

39-
To allow immovable types to be contained in movable types, we introduce a `core::cell::MobileCell` wrapper which itself implements `Move`. It works similarly to `Cell` in that it disallows references to the value inside.
58+
let a = Foo {
59+
field: true
60+
};
61+
address(&a)
62+
```
63+
We can also find the address of `a.field` in our `address function using this code:
4064
```rust
41-
#[lang = "mobile_cell"]
42-
pub struct MobileCell<T: ?Move> {
43-
value: T,
65+
fn address(v: &Foo) -> usize {
66+
&v.field as *const _ as usize
4467
}
68+
```
69+
Thus we say that our borrow `&a` observes both `a` and `a.field`.
70+
In general, borrowing a value observes all other values stored inside except for values which are reached using an indirection (for example a reference or a `Box`). If any elements of an array is observed, the entire array is also observed.
4571

46-
unsafe impl<T: ?Move> Move for MobileCell<T> {}
72+
Whenever we are moving an value we emit an error if the type does implement `Move` and the value could have been observed. We also do the same check for any values stored inside; again ignoring indirections.
4773

48-
impl<T: ?Move> MobileCell<T> {
49-
pub const fn new(value: T) -> Movable<T> {
50-
Movable {
51-
value: value,
52-
}
53-
}
74+
## Immovable types contained in movable types
5475

55-
pub fn into_inner(self) -> T {
56-
self.value
57-
}
76+
To allow immovable types to be contained in movable types, we introduce a `core::cell::MovableCell` wrapper which itself implements `Move`. It works similarly to `Cell` in that it disallows references to the value inside.
77+
```rust
78+
#[lang = "movable_cell"]
79+
#[derive(Default)]
80+
pub struct MovableCell<T: ?Move> {
81+
value: T,
82+
}
5883

59-
pub fn replace(&mut self, new_value: T) -> T {
60-
let mut result = MobileCell::new(new_value);
61-
core::mem::replace(self, &mut result);
62-
result.into_inner()
63-
}
84+
impl<T: ?Move> MovableCell<T> {
85+
/// Creates a new MovableCell.
86+
pub const fn new(value: T) -> Self {
87+
MovableCell {
88+
value: value,
89+
}
90+
}
91+
92+
/// Extracts the inner value.
93+
pub fn into_inner(self) -> T {
94+
self.value
95+
}
96+
97+
/// Replaces the inner value.
98+
pub fn replace(&mut self, new_value: T) -> T {
99+
mem::replace(self, MovableCell::new(new_value)).into_inner()
100+
}
64101
}
65102
```
66103

@@ -86,7 +123,6 @@ In general, we can allow immovable types in an movable container if we either:
86123
- disallow all methods of accessing the address of the contained immovable types, including references (possible for `Vec`, `HashMap`)
87124
- prevent the type from actually moving once it's inside (the method suitable for `Box`, `Rc`, `Arc`)
88125

89-
90126
# How We Teach This
91127
[how-we-teach-this]: #how-we-teach-this
92128

0 commit comments

Comments
 (0)