about summary refs log tree commit diff
path: root/src/doc/rustc-dev-guide
diff options
context:
space:
mode:
authorMatthew Jasper <mjjasper1@gmail.com>2019-04-24 20:53:08 +0100
committerWho? Me?! <mark-i-m@users.noreply.github.com>2019-04-24 17:29:05 -0500
commitb90995e153166e37b6ac01fc896139726ec65f3c (patch)
tree1aa621550f2145dc31ec8f394388bcb7bddff297 /src/doc/rustc-dev-guide
parente7ca811a65b4136f8860714751883ce4b959fb08 (diff)
downloadrust-b90995e153166e37b6ac01fc896139726ec65f3c.tar.gz
rust-b90995e153166e37b6ac01fc896139726ec65f3c.zip
Add documentation for two-phase borrows
Diffstat (limited to 'src/doc/rustc-dev-guide')
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md1
-rw-r--r--src/doc/rustc-dev-guide/src/borrow_check/two_phase_borrows.md100
2 files changed, 101 insertions, 0 deletions
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index 099c65a738f..477b9a7073d 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -76,6 +76,7 @@
       - [Move paths](./borrow_check/moves_and_initialization/move_paths.md)
     - [MIR type checker](./borrow_check/type_check.md)
     - [Region inference](./borrow_check/region_inference.md)
+    - [Two-phase-borrows](./borrow_check/two_phase_borrows.md)
 - [Constant evaluation](./const-eval.md)
     - [miri const evaluator](./miri.md)
 - [Parameter Environments](./param_env.md)
diff --git a/src/doc/rustc-dev-guide/src/borrow_check/two_phase_borrows.md b/src/doc/rustc-dev-guide/src/borrow_check/two_phase_borrows.md
new file mode 100644
index 00000000000..753ffa5b286
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/borrow_check/two_phase_borrows.md
@@ -0,0 +1,100 @@
+# Two-phase borrows
+
+Two-phase borrows are a more permissive version of mutable borrows that allow
+nested method calls such as `vec.push(vec.len())`. Such borrows first act as
+shared borrows in a "reservation" phase and can later be "activated" into a
+full mutable borrow.
+
+Only certain implicit mutable borrows can be two-phase, any `&mut` or `ref mut`
+in the source code is never a two-phase borrow. The cases where we generate a
+two-phase borrow are:
+
+1. The autoref borrow when calling a method with a mutable reference receiver.
+2. A mutable reborrow in function arguments.
+3. The implicit mutable borrow in an overloaded compound assignment operator.
+
+To give some examples:
+
+```rust
+// In the source code
+
+// Case 1:
+let mut v = Vec::new();
+v.push(v.len());
+let r = &mut Vec::new();
+r.push(r.len());
+
+// Case 2:
+std::mem::replace(r, vec![1, r.len()]);
+
+// Case 3:
+let mut x = std::num::Wrapping(2);
+x += x;
+```
+
+Expanding these enough to show the two-phase borrows:
+
+```rust,ignore
+// Case 1:
+let mut v = Vec::new();
+let temp1 = &two_phase v;
+let temp2 = v.len();
+Vec::push(temp1, temp2);
+let r = &mut Vec::new();
+let temp3 = &two_phase *r;
+let temp4 = r.len();
+Vec::push(temp3, temp4);
+
+// Case 2:
+let temp5 = &two_phase *r;
+let temp6 = vec![1, r.len()];
+std::mem::replace(temp5, temp6);
+
+// Case 3:
+let mut x = std::num::Wrapping(2);
+let temp7 = &two_phase x;
+let temp8 = x;
+std::ops::AddAssign::add_assign(temp7, temp8);
+```
+
+Whether a borrow can be two-phase is tracked by a flag on the [`AutoBorrow`]
+after type checking, which is then [converted] to a [`BorrowKind`] during MIR
+construction.
+
+Each two-phase borrow is assigned to a temporary that is only used once. As
+such we can define:
+
+* The point where the temporary is assigned to is called the *reservation*
+  point of the two-phase borrow.
+* The point where the temporary is used, which is effectively always a
+  function call, is called the *activation* point.
+
+The activation points are found using the [`GatherBorrows`] visitor. The
+[`BorrowData`] then holds both the reservation and activation points for the
+borrow.
+
+[`AutoBorrow`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/adjustment/enum.AutoBorrow.html
+[converted]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/hair/cx/expr/trait.ToBorrowKind.html#method.to_borrow_kind
+[`BorrowKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/mir/enum.BorrowKind.html
+[`GatherBorrows`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/mir/visit/trait.Visitor.html#method.visit_local
+[`BorrowData`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/borrow_set/struct.BorrowData.html
+
+## Checking two-phase borrows
+
+Two-phase borrows are treated as if they were mutable borrows with the
+following exceptions:
+
+1. At every location in the MIR we [check] if any two-phase borrows are
+   activated at this location. If a live two phase borrow is activated at a
+   location, then we check that there are no borrows that conflict with the
+   two-phase borrow.
+2. At the reservation point we error if there are conflicting live *mutable*
+   borrows. And lint if there are any conflicting shared borrows.
+3. Between the reservation and the activation point, the two-phase borrow acts
+   as a shared borrow. We determine (in [`is_active`]) if we're at such a point
+   by using the [`Dominators`] for the MIR graph.
+4. After the activation point, the two-phase borrow acts as a mutable borrow.
+
+[check]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/struct.MirBorrowckCtxt.html#method.check_activations
+[`Dominators`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/graph/dominators/struct.Dominators.html
+[`is_active`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/path_utils/fn.is_active.html