]>
Commit | Line | Data |
---|---|---|
48663c56 XL |
1 | # Two-phase borrows |
2 | ||
3 | Two-phase borrows are a more permissive version of mutable borrows that allow | |
4 | nested method calls such as `vec.push(vec.len())`. Such borrows first act as | |
5 | shared borrows in a "reservation" phase and can later be "activated" into a | |
6 | full mutable borrow. | |
7 | ||
8 | Only certain implicit mutable borrows can be two-phase, any `&mut` or `ref mut` | |
9 | in the source code is never a two-phase borrow. The cases where we generate a | |
10 | two-phase borrow are: | |
11 | ||
12 | 1. The autoref borrow when calling a method with a mutable reference receiver. | |
13 | 2. A mutable reborrow in function arguments. | |
14 | 3. The implicit mutable borrow in an overloaded compound assignment operator. | |
15 | ||
16 | To give some examples: | |
17 | ||
dc9dc135 | 18 | ```rust2018 |
48663c56 XL |
19 | // In the source code |
20 | ||
21 | // Case 1: | |
22 | let mut v = Vec::new(); | |
23 | v.push(v.len()); | |
24 | let r = &mut Vec::new(); | |
25 | r.push(r.len()); | |
26 | ||
27 | // Case 2: | |
28 | std::mem::replace(r, vec![1, r.len()]); | |
29 | ||
30 | // Case 3: | |
31 | let mut x = std::num::Wrapping(2); | |
32 | x += x; | |
33 | ``` | |
34 | ||
35 | Expanding these enough to show the two-phase borrows: | |
36 | ||
37 | ```rust,ignore | |
38 | // Case 1: | |
39 | let mut v = Vec::new(); | |
40 | let temp1 = &two_phase v; | |
41 | let temp2 = v.len(); | |
42 | Vec::push(temp1, temp2); | |
43 | let r = &mut Vec::new(); | |
44 | let temp3 = &two_phase *r; | |
45 | let temp4 = r.len(); | |
46 | Vec::push(temp3, temp4); | |
47 | ||
48 | // Case 2: | |
49 | let temp5 = &two_phase *r; | |
50 | let temp6 = vec![1, r.len()]; | |
51 | std::mem::replace(temp5, temp6); | |
52 | ||
53 | // Case 3: | |
54 | let mut x = std::num::Wrapping(2); | |
55 | let temp7 = &two_phase x; | |
56 | let temp8 = x; | |
57 | std::ops::AddAssign::add_assign(temp7, temp8); | |
58 | ``` | |
59 | ||
60 | Whether a borrow can be two-phase is tracked by a flag on the [`AutoBorrow`] | |
61 | after type checking, which is then [converted] to a [`BorrowKind`] during MIR | |
62 | construction. | |
63 | ||
64 | Each two-phase borrow is assigned to a temporary that is only used once. As | |
65 | such we can define: | |
66 | ||
67 | * The point where the temporary is assigned to is called the *reservation* | |
68 | point of the two-phase borrow. | |
69 | * The point where the temporary is used, which is effectively always a | |
70 | function call, is called the *activation* point. | |
71 | ||
72 | The activation points are found using the [`GatherBorrows`] visitor. The | |
73 | [`BorrowData`] then holds both the reservation and activation points for the | |
74 | borrow. | |
75 | ||
ba9703b0 | 76 | [`AutoBorrow`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/adjustment/enum.AutoBorrow.html |
6a06907d | 77 | [converted]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/cx/expr/trait.ToBorrowKind.html#method.to_borrow_kind |
ba9703b0 XL |
78 | [`BorrowKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.BorrowKind.html |
79 | [`GatherBorrows`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/visit/trait.Visitor.html#method.visit_local | |
48663c56 XL |
80 | [`BorrowData`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/borrow_set/struct.BorrowData.html |
81 | ||
82 | ## Checking two-phase borrows | |
83 | ||
84 | Two-phase borrows are treated as if they were mutable borrows with the | |
85 | following exceptions: | |
86 | ||
87 | 1. At every location in the MIR we [check] if any two-phase borrows are | |
88 | activated at this location. If a live two phase borrow is activated at a | |
89 | location, then we check that there are no borrows that conflict with the | |
90 | two-phase borrow. | |
91 | 2. At the reservation point we error if there are conflicting live *mutable* | |
92 | borrows. And lint if there are any conflicting shared borrows. | |
93 | 3. Between the reservation and the activation point, the two-phase borrow acts | |
94 | as a shared borrow. We determine (in [`is_active`]) if we're at such a point | |
95 | by using the [`Dominators`] for the MIR graph. | |
96 | 4. After the activation point, the two-phase borrow acts as a mutable borrow. | |
97 | ||
98 | [check]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/struct.MirBorrowckCtxt.html#method.check_activations | |
99 | [`Dominators`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/graph/dominators/struct.Dominators.html | |
100 | [`is_active`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/path_utils/fn.is_active.html |