# Purely forward propagation ## Intuition * A *loan* is a reference to a place in memory and the conditions it was borrowed (shared, mutable) * An *origin* is a set of loans {L}, such that if a reference has origin O, then we know that: * invaliding one of the loans in {L} may invalidate the reference * actions that do not invalidate any of the loans in {L} cannot invalidate the reference * When we have an access that invalidate some loan L: * We mark any origins that contain L at that point as invalidated * Questions Niko is not sure about * Do we still have to "propagate" subset etc? * What's the best way to think about that :) ## Tentative idea ### State we compute * `subset(O1, O2, N)` -- `O1 <= O2` must hold on entry to the node `N` * `loan_in_origin(L, O1, N)` -- the origin `O` contains the loan `L` on entry to the node `N` * `origin_invalidated(O, N)` -- data with origin `O` is invalidated on entry to the node N ### End goal we are searching for * `invalidated_origin_accessed(O, N)` -- origin `O` which is invalidated was accessed; error ## Rules ### Inputs * `access(O, N)`: access data with origin `O` * `invalidate_loan(L, N)`: invalidate the loan `L` at the node `N` * `clear_loan(L, N)`: the path named in `L` changed without invalidating the underlying memory; subsequence invalidations of `L` do not affect existing references * `clear_origin(O, N)`: all references with origin `O` was overwritten * `introduce_loan(L, O, N)`: introduce the loan `L` into origin `O` at node `N` * Every borrow `&'a place` or `&'a mut place` introduces, for some unique origin `'a`: * one loan in `'a` for the borrow itself * various subset relationships between each origin `O` dereferenced by place and `'a` * `introduce_subset(O1, O2, N)`: require that `O1 <= O2` at node `N` * `cfg_edge(N1, N2)`: control-flow graph edge Note the convention: Input facts are in the imperative mood ("invalidate loan") while computed facts use the past participle ("loan invalidated"). ### Assumptions about ordering For a given node `N`... * First we perform any accesses `access(O, N)` * Then we invalidate any loans `invalidate_loan(L, N)` * Then we clear any loans or origins * Then we introduce any loans, subsets, etc * Then we remove things from scopes This corresponds to ``` place = expr ``` * Evaluating the expr accesses the origins (including potentially some in place) * Then place is overwritten, invalidating and clearing loans * Storing the value also creates subtyping relationships between the type of the value that was stored and the place it was stored into ### Computing subset The `subset(O1, O2, N)` relation indicates that `O1 <= O2` on entry to node `N` ``` subset(O1, O2, N2) :- // Introduced by predecessor cfg_edge(N1, N2), introduce_subset(O1, O2, N1) subset(O1, O2, N2) :- // Carried over from predecessor cfg_edge(N1, N2), subset(O1, O2, N1), !clear_origin(O1, N1), !clear_origin(O2, N1). subset(O1, O3, N1) :- // Transitive closure subset(O1, O2, N1), subset(O2, O3, N1). ``` ### Computing loans in an origin The `loan_in_origin(L, O, N)` relation indicates that the origin `O` contains the node `L` on entry to node `N` ``` loan_in_origin(L, O, N2) :- // Introduced by predecessor cfg_edge(N1, N2), introduce_loan(L, O, N1) loan_in_origin(L, O, N2) :- // Carried over from predecessor cfg_edge(N1, N2), loan_in_origin(L, O, N1), !clear_loan(L, N1), !clear_origin(O, N1), !invalidate_loan(L, N1). loan_in_origin(L, O2, N) :- // Transitive closure subset(O1, O2, N), loan_in_origin(L, O1, N). ``` ### Computing invalidated origins An *origin* is invalidated if it contains some *loan* that has been invalidated. Invalidating a loan is an input fact, `loan_invalidated`, and it takes effect during a node's execution. So we look for origins that (a) contained a loan `L` on entry to some node `N` and (b) the node invalidated the loan `L`. ``` origin_invalidated(O, N2) :- // Introduced by predecessor cfg_edge(N1, N2), loan_in_origin(L, O, N1), invalidate_loan(L, N1). ``` ### Error: access to invalidated origins An error occurs when some node N tries to access data with origin O, but O is invalidated on entry to N. ``` invalidated_origin_accessed(O, N) :- // Introduced by predecessor access(O, N), origin_invalidated(O, N). ``` ## Fact Generation ### Accesses Any read (or write) of a place `P` counts as an access of * any origin in the type of `P`. * For example, using a `&'outer Vec<&'inner u32>` accesses both `'outer` and `'inner`. * any origin that is dereferenced in `P`, or, more precisely, any origin in the type of any reference that is dereferenced in `P`. * For example, reading from `(*x).0`, where `x: &'x0 (&'x1 u32, &'x2 u32)` accesses * `'x0` because of `*x` * `'x1` because of the `.0`, which gives the final type `&'x1 u32` Mutably borrowing `P` counts as a write to `P` in this context, immutable borrows count as a read. ### Invalidation If a read or write violates an existing borrow `'L`, then `'L` is invalidated. * For example, reading `x` invalidates and mutable borrow `'L_mut_x` of `x`. #### Questions * Does borrowing `&'L_p mut p` always invalidate + clear `'L_p`, because `p` is written to (mutably borrowed), but the borrow is also re-created? **Answer:** Yes, because the invalidation happens conceptually *before* the clear and may be propagated across subsets. * Should writing to `*y`, where `y = &'L_*x mut *x`, `x = &'L_p mut p`, `p: u32`, invalidate `'L_*x`, `'L_p`, or both? How do we know if `x` still references `p` at the time of the write? **Answer:** usage of a path invalidates any origin whose associated path starts with the used path. So using `*y` invalidates **neither** `'L_*x` **nor** `'L_p`, but **does** invalidate any `'L_y`, `'L_*y` where they exist. ```rust let mut x = 42; let p = &mut x; let pp = &mut *p; // reborrow `p` // `p` is no longer used. How is `pp` (the reborrow) connected to `x`? x = 0; dbg!(pp); ``` * I think we would need to propagate `access` across the subset graph as well? -Dylan * How do we handle assignments that invalidate unrelated origins? Example `diamond-ref-mod` (Dylan): ```rust let mut x = 4; let mut p = &22; if maybe() { p = &x; } else { x = 0; // invalidate x } dbg!(p); // access p (or *p?) ``` * Here, there can only ever be **either** a relationship between `p` and `x`, **or** a mutation of `x`, but not both. However, both the subset relationship and the invalidation from the write reach the access of `p`. * Current idea: only invalidate origins from which the invalidation is CFG-reachable. **Answer:** this is solved by removing the transitive closure on `origin_invalidated` and `subset` in favor of propagating `invalidate_origin` across subsets in the original node. This has, however, caused problems in `issue-47680`. * How do we handle self-invalidation? Example `self-invalidation-loop` (Dylan): ```rust fn main() { let mut x = 42; let mut v: Vec<&mut i32> = vec![]; while maybe() { let tmp = &mut x; // L_x v.push(tmp); } } ``` * Here, the problem is that the lifetime of `tmp`'s `'L_x` on one iteration is **extended** into the next by storing it in `v`. This makes it not match lexical scopes anymore. However, when `tmp` is re-created, this clears `'L_x`, so neither additional subset relations nor invalidation are propagated. * Is this possible to solve without liveness (of `v` or `'tmp`)? **Answer:** this is also solved by intra-node propagation of `invalidate_origin` before any corresponding clears for that origin. ### Clearing Origins Origins are cleared * when they are created. * For example, taking a borrow `&'L_P P` of `P` clears `&'L_P`. * when expressions in the path of a loan origin change, but its target and the associated memory don't. * Consider the following code ```rust let x = 42; let y = 22; let mut temp = &x; let p = &*temp; // &'L_*temp *temp == &x temp = &y; // now `*temp != x`, but `*p == x` and `x` still exists. // Therefore, `p` and `'L_*temp` are not invalidated, but // future occurences of `*temp` are unrelated to `p`. ``` ### Subset Relations Relationships between origins arise from borrows and assignments. #### Borrows * **Immutably** borrowing `P = *Q` as `&'L_*Q *Q` where `Q` is a **shared** reference `Q: &'Q T` introduces `'L_*Q <= 'Q`, because the outer reference `'L_*Q` cannot outlive the inner reference `'Q`. * **Immutably** borrowing `P = *Q` as `&'L_*Q *Q` where `Q` is a **mutable** reference `Q: &'Q mut T` introduces `'L_*Q <= 'Q` for the same reason, but continues recursively adding borrow subset relations for `Q` (i.e., if `Q = *R`, then consider origins in `R`). * This is not necessary for shared refs because they are `Copy`, while mutable refs mean the original source cannot be used while the new borrow is live. * **Mutably** borrowing `P = *Q` as `&'L_*Q mut *Q` where `Q` is a **shared** reference `Q: &'Q T` is an error (mutable borrow of shared content). * **Mutably** borrowing `P = *Q` as `&'L_*Q mut *Q` where `Q` is a **mutable** reference `Q: &'Q mut T` has the same borrow constraints as immutable borrows of mutable content (`'L_*Q <= 'Q` and adding constraints recursively). #### Types Assignments of values whose types include origins introduces subset constraints according to **variance**. | Type | `'a` | `T` | `U` | | ------------------------ | --------- | ------------- | --------- | | `&'a T` and `*const T` | covariant | covariant | | | `&'a mut T` and `*mut T` | covariant | invariant | | | `Box<T>` and `Vec<T>` | | covariant | | | `[Unsafe]Cell<T>` | | invariant | | | `fn(T) -> U` | | contravariant | covariant | For example, assigning `tmp = &'L_v mut v`, where `v: Vec<&'v mut u32>` and `tmp: &'tmp0 mut Vec<&'tmp1 mut u32>` introduces * `'L_v <= 'tmp0` because of covariance of the reference in `'a` * `'v <= 'tmp1` **and** `'tmp1 <= 'v` because of invariance of the mutable reference in `T` (even though these are again lifetimes in a reference) #### Bounds Additional constraints may arise from `where` bounds. ## Examples ### Example A ```rust= let mut x = 3; let y: &u32 = &x; x = 4; drop(y); ``` #### Input relations #### CFG ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] Decls [ label = <<table border="0"> <tr><td align="left">let x:</td> <td align="left">u32</td></tr> <tr><td align="left">let y:</td> <td align="left">&amp;'y u32</td></tr> <tr><td align="left">L0:</td> <td align="left">shared(x)</td></tr> </table>> ] A [ label = <<table border="0"> <tr><td>A: x = 3</td></tr> <tr><td>invalidate_loan(L0)</td></tr> </table>> ] B [ label = <<table border="0"> <tr><td>B: y = &amp;'0 x</td></tr> <tr><td>introduce_loan(L0, '0)</td></tr> <tr><td>introduce_subset('0, 'y)</td></tr> </table>> ] A -> B C [ label = <<table border="0"> <tr><td>C: x = 4</td></tr> <tr><td>invalidate_loan(L0)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('0, 'y)</td></tr> <tr><td>loan_in_origin(L0, '0)</td></tr> <tr><td>loan_in_origin(L0, 'y)</td></tr> </table>> ] B -> C D [ label = <<table border="0"> <tr><td>D: drop(y)</td></tr> <tr><td>access('y)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('0, 'y)</td></tr> <tr><td>origin_invalidated('0)</td></tr> <tr><td>origin_invalidated('y)</td></tr> <tr><td>invalidated_origin_accessed('y)</td></tr> </table>> ] C -> D } ``` ### Example #47680 ```rust #![feature(nll)] struct Thing; impl Thing { fn maybe_next(&mut self) -> Option<&mut Self> { None } } fn main() { let mut temp = &mut Thing; loop { match temp.maybe_next() { Some(v) => { temp = v; } None => { } } } } ``` ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] Decls [ label = <<table border="0"> <tr><td align="left">let temp:</td> <td align="left">&amp;'temp mut Thing</td></tr> <tr><td align="left">let t0:</td> <td align="left">&amp;'t0 mut Thing</td></tr> <tr><td align="left">let v:</td> <td align="left">&amp;'v mut Thing</td></tr> <tr><td align="left">L_Thing:</td> <td align="left">mutable(Thing)</td></tr> <tr><td align="left">L_*temp:</td> <td align="left">mutable(*temp)</td></tr> </table>> ] A [ label = <<table border="0"> <tr><td>temp = &amp;'0 mut Thing</td></tr> <tr><td>clear_loan(L_temp)</td></tr> <tr><td>introduce_loan(L_Thing, '0)</td></tr> <tr><td>introduce_subset('0, 'temp)</td></tr> <tr><td>-------------------</td></tr> </table>> ] B [ label = <<table border="0"> <tr><td>t0 = &amp;'1 mut *temp</td></tr> <tr><td>clear_loan('t0)</td></tr> <tr><td>introduce_loan(L_*temp, '1)</td></tr> <tr><td>introduce_subset('temp, '1)</td></tr> <tr><td>introduce_subset('1, 't0)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('0, 'temp)</td></tr> <tr><td>loan_in_origin(L_Thing, '0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'temp)</td></tr> </table>> ] A -> B C [ label = <<table border="0"> <tr><td>v = MaybeNext(t0)</td></tr> <tr><td>clear_origin('v)</td></tr> <tr><td>introduce_subset('t0, 'v)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('0, 'temp)</td></tr> <tr><td>subset('0, '1)</td></tr> <tr><td>subset('0, 't0)</td></tr> <tr><td>subset('temp, '1)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('1, 't0)</td></tr> <tr><td>loan_in_origin(L_Thing, '0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'temp)</td></tr> <tr><td>loan_in_origin(L_Thing, '1)</td></tr> <tr><td>loan_in_origin(L_Thing, 't0)</td></tr> <tr><td>loan_in_origin(L_*temp, '1)</td></tr> <tr><td>loan_in_origin(L_*temp, 't0)</td></tr> </table>> ] B -> C D [ label = <<table border="0"> <tr><td>temp = v</td></tr> <tr><td>clear_loan(L_*temp)</td></tr> <tr><td>clear_origin('temp)</td></tr> <tr><td>introduce_subset('v, 'temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('0, 'temp)</td></tr> <tr><td>subset('0, '1)</td></tr> <tr><td>subset('0, 't0)</td></tr> <tr><td>subset('0, 'v)</td></tr> <tr><td>subset('temp, '1)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('1, 't0)</td></tr> <tr><td>subset('1, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>loan_in_origin(L_Thing, '0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'temp)</td></tr> <tr><td>loan_in_origin(L_Thing, '1)</td></tr> <tr><td>loan_in_origin(L_Thing, 't0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'v)</td></tr> <tr><td>loan_in_origin(L_*temp, '1)</td></tr> <tr><td>loan_in_origin(L_*temp, 't0)</td></tr> <tr><td>loan_in_origin(L_*temp, 'v)</td></tr> </table>> ] C -> D E [ label = <<table border="0"> <tr><td>-------------------</td></tr> <tr><td>subset('0, 'temp)</td></tr> <tr><td>subset('0, '1)</td></tr> <tr><td>subset('0, 't0)</td></tr> <tr><td>subset('0, 'v)</td></tr> <tr><td>subset('temp, '1)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('1, 't0)</td></tr> <tr><td>subset('1, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>loan_in_origin(L_Thing, '0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'temp)</td></tr> <tr><td>loan_in_origin(L_Thing, '1)</td></tr> <tr><td>loan_in_origin(L_Thing, 't0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'v)</td></tr> <tr><td>loan_in_origin(L_*temp, '1)</td></tr> <tr><td>loan_in_origin(L_*temp, 't0)</td></tr> <tr><td>loan_in_origin(L_*temp, 'v)</td></tr> </table>> ] C -> E F [ label = <<table border="0"> <tr><td>-------------------</td></tr> <tr><td>subset('0, '1)</td></tr> <tr><td>subset('0, 't0)</td></tr> <tr><td>subset('0, 'v)</td></tr> <tr><td>subset('0, 'temp)</td></tr> <tr><td>subset('1, 't0)</td></tr> <tr><td>subset('1, 'v)</td></tr> <tr><td>subset('1, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>loan_in_origin(L_Thing, '0)</td></tr> <tr><td>loan_in_origin(L_Thing, '1)</td></tr> <tr><td>loan_in_origin(L_Thing, 't0)</td></tr> <tr><td>loan_in_origin(L_Thing, 'v)</td></tr> <tr><td>loan_in_origin(L_Thing, 'temp)</td></tr> </table>> ] E -> F D -> F F -> C } ``` ### Example vec-temp This illustrates why we propagate subtype relations. ```rust let mut x = 22; let mut v = vec![]; let p = &x; let mut tmp = &mut v; Vec::push(tmp, p); x = 23; Vec::len(v); // ERROR ``` ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] Decls [ label = <<table border="0"> <tr><td align="left">let x:</td> <td align="left">u32</td></tr> <tr><td align="left">let v:</td> <td align="left">Vec&lt;&amp;'v mut u32&gt;</td></tr> <tr><td align="left">let p:</td> <td align="left">&amp;'p u32</td></tr> <tr><td align="left">let tmp:</td> <td align="left">&amp;'tmp0 mut Vec&lt;&amp;'tmp1 mut u32&gt;</td></tr> <tr><td align="left">L_x:</td> <td align="left">shared(x)</td></tr> <tr><td align="left">L_v:</td> <td align="left">mutable(v)</td></tr> </table>> ] A [ label = <<table border="0"> <tr><td>x = 22</td></tr> <tr><td>invalidate_loan(L_x)</td></tr> </table>> ] B [ label = <<table border="0"> <tr><td>v = vec![]</td></tr> <tr><td>invalidate_loan(L_v)</td></tr> </table>> ] A -> B C [ label = <<table border="0"> <tr><td>p = &amp;x</td></tr> <tr><td>introduce_loan(L_x, 'p)</td></tr> </table>> ] B -> C D [ label = <<table border="0"> <tr><td>tmp = &amp;mut v</td></tr> <tr><td>introduce_loan('tmp0, L_v)</td></tr> <tr><td>introduce_subset('tmp1, 'v)</td></tr> <tr><td>introduce_subset('v, 'tmp1)</td></tr> <tr><td>-------------------</td></tr> <tr><td>loan_in_origin(L_x, 'p)</td></tr> </table>> ] C -> D E [ label = <<table border="0"> <tr><td>Vec::push(tmp, p)</td></tr> <tr><td>access_origin('tmp0)</td></tr> <tr><td>access_origin('tmp1)</td></tr> <tr><td>access_origin('p)</td></tr> <tr><td>introduce_subset('p, 'tmp1)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('tmp1, 'v)</td></tr> <tr><td>subset('v, 'tmp1)</td></tr> <tr><td>loan_in_origin(L_v, 'tmp0)</td></tr> <tr><td>loan_in_origin(L_x, 'p)</td></tr> </table>> ] D -> E F [ label = <<table border="0"> <tr><td>x = 23</td></tr> <tr><td>invalidate_loan(L_x)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('tmp1, 'v)</td></tr> <tr><td>subset('v, 'tmp1)</td></tr> <tr><td>subset('p, 'tmp1)</td></tr> <tr><td>loan_in_origin(L_v, 'tmp0)</td></tr> <tr><td>loan_in_origin(L_x, 'p)</td></tr> <tr><td>loan_in_origin(L_x, 'tmp1)</td></tr> <tr><td>loan_in_origin(L_x, 'v)</td></tr> </table>> ] E -> F G [ label = <<table border="0"> <tr><td>Vec::len(v)</td></tr> <tr><td>access_origin('v)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('tmp1, 'v)</td></tr> <tr><td>subset('v, 'tmp1)</td></tr> <tr><td>subset('p, 'tmp1)</td></tr> <tr><td>origin_invalidated('tmp1)</td></tr> <tr><td>origin_invalidated('v)</td></tr> <tr><td>invalidated_origin_accessed('v)</td></tr> </table>> ] F -> G } ``` ### Example killing-and-murder ```rust let p = 22; let q = 44; let x: &mut i32 = &mut p; // `x` points at `p` let y = &mut *x; // Loan L0, `y` points at `p` too // ... x = &mut q; // `x` points at `q`; clears L0 ``` ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] Decls [ label = <<table border="0"> <tr><td align="left">let p:</td> <td align="left">u32</td></tr> <tr><td align="left">let q:</td> <td align="left">u32</td></tr> <tr><td align="left">let x:</td> <td align="left">&amp;'x mut i32</td></tr> <tr><td align="left">let y:</td> <td align="left">&amp;'y mut i32</td></tr> <tr><td align="left">L_p:</td> <td align="left">mut(p)</td></tr> <tr><td align="left">L_q:</td> <td align="left">mut(q)</td></tr> <tr><td align="left">L_*x:</td> <td align="left">mut(*x)</td></tr> </table>> ] A [ label = <<table border="0"> <tr><td>p = 22</td></tr> <tr><td>invalidate_loan(L_p)</td></tr> </table>> ] B [ label = <<table border="0"> <tr><td>q = 22</td></tr> <tr><td>invalidate_loan(L_q)</td></tr> </table>> ] A -> B C [ label = <<table border="0"> <tr><td>x = &amp;'0 mut p</td></tr> <tr><td>invalidate_loan(L_q)</td></tr> </table>> ] B -> C } ``` ### Example Dylan's Canonical Liveness ```rust let x = 22; let y = 44; let mut p: &'0 i32 = &'1 x; // Loan L0 p = &'3 y; // Loan L1 x += 1; // invalidates(L0) print(*p); ``` ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] Decls [ label = <<table border="0"> <tr><td align="left">let p:</td> <td align="left">u32</td></tr> <tr><td align="left">let q:</td> <td align="left">u32</td></tr> <tr><td align="left">let x:</td> <td align="left">&amp;'x mut i32</td></tr> <tr><td align="left">let y:</td> <td align="left">&amp;'y mut i32</td></tr> <tr><td align="left">L_p:</td> <td align="left">mut(p)</td></tr> <tr><td align="left">L_q:</td> <td align="left">mut(q)</td></tr> <tr><td align="left">L_*x:</td> <td align="left">mut(*x)</td></tr> </table>> ] A [ label = <<table border="0"> <tr><td>p = 22</td></tr> <tr><td>invalidate_loan(L_p)</td></tr> </table>> ] B [ label = <<table border="0"> <tr><td>q = 22</td></tr> <tr><td>invalidate_loan(L_q)</td></tr> </table>> ] A -> B } ``` --- ### Example vec-push-ref ```rust let mut x = 22; let mut v = vec![]; let p = &x; if something() { v.push(p); x += 1; v.len(); // ERROR } else { x += 1; v.len(); // OK } v.len(); // ERROR ``` ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] Decls [ label = <<table border="0"> <tr><td align="left">let x:</td> <td align="left">u32</td></tr> <tr><td align="left">let v:</td> <td align="left">Vec&lt;&amp;'v mut u32&gt;</td></tr> <tr><td align="left">let p:</td> <td align="left">&amp;'p u32</td></tr> <tr><td align="left">L_x:</td> <td align="left">shared(x)</td></tr> <tr><td align="left">L_v:</td> <td align="left">mutable(*v)</td></tr> </table>> ] } ``` ### Example eq-ftw ```rust #![feature(nll)] #![allow(unused_variables, unused_assignments)] fn random_bool() -> bool { unimplemented!() } fn main() { let mut x: (&u32,) = (&0,); let mut y: (&u32,) = (&1,); let mut z = 2; if random_bool() { y.0 = x.0; // creates `'x: 'y` subset relation } if random_bool() { x.0 = &z; // creates {L0} in 'x constraint // this point, we have `'x: 'y` and `{L0} in `'x`, so we also have `{L0} in 'y` drop(x.0); } z += 1; // polonius flags an (unnecessary) error drop(y.0); } ``` ### Example propagation required ```rust fn cfg_propagation_required(x: &mut &i32) { let y = x; *y = &g(); } ``` ## 47680 unrolled ```graphviz digraph G { rankdir = "TD" node [ shape = "rectangle" ] a [ label = <<table border="0"> <tr><td>a: temp = &amp;'L_Thing mut Thing</td></tr> <tr><td>-------------------</td></tr> <tr><td>introduce_subset('L_Thing, 'temp)</td></tr> </table>> ] b [ label = <<table border="0"> <tr><td>b: t0 = &amp;'L_*temp mut *temp</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('temp)</td></tr> <tr><td>clear_origin('L_*temp)</td></tr> <tr><td>clear_origin('t0)</td></tr> <tr><td>introduce_subset('L_*temp, 't0)</td></tr> <tr><td>introduce_subset('temp, 'L_*temp)</td></tr> <tr><td>invalidate_origin('L_*temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> </table>> ] a -> b b1 [ label = <<table border="0"> <tr><td>b1: t0 = &amp;'L_*temp mut *temp</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('temp)</td></tr> <tr><td>clear_origin('L_*temp)</td></tr> <tr><td>clear_origin('t0)</td></tr> <tr><td>introduce_subset('L_*temp, 't0)</td></tr> <tr><td>introduce_subset('temp, 'L_*temp)</td></tr> <tr><td>invalidate_origin('L_*temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_*temp, 'L_*temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'temp)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'L_*temp)</td></tr> <tr><td>subset('t0, 't0)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] f -> b1 b2 [ label = <<table border="0"> <tr><td>b2: t0 = &amp;'L_*temp mut *temp</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('temp)</td></tr> <tr><td>clear_origin('L_*temp)</td></tr> <tr><td>clear_origin('t0)</td></tr> <tr><td>introduce_subset('L_*temp, 't0)</td></tr> <tr><td>introduce_subset('temp, 'L_*temp)</td></tr> <tr><td>invalidate_origin('L_*temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td bgcolor= "yellow">invalidated_origin_accessed('temp)</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 'L_*temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'temp)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'L_*temp)</td></tr> <tr><td>subset('t0, 't0)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] f1 -> b2 b3 [ label = <<table border="0"> <tr><td>b3: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 'L_*temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'temp)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'L_*temp)</td></tr> <tr><td>subset('t0, 't0)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] f2 -> b3 c [ label = <<table border="0"> <tr><td>c: v = MaybeNext(t0)</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('t0)</td></tr> <tr><td>clear_origin('v)</td></tr> <tr><td>introduce_subset('t0, 'v)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> </table>> ] b -> c c1 [ label = <<table border="0"> <tr><td>c1: v = MaybeNext(t0)</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('t0)</td></tr> <tr><td>clear_origin('v)</td></tr> <tr><td>introduce_subset('t0, 'v)</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>origin_invalidated('v)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] b1 -> c1 c2 [ label = <<table border="0"> <tr><td>c2: v = MaybeNext(t0)</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('t0)</td></tr> <tr><td>clear_origin('v)</td></tr> <tr><td>introduce_subset('t0, 'v)</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>origin_invalidated('v)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] b2 -> c2 d [ label = <<table border="0"> <tr><td>d: temp = v</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('v)</td></tr> <tr><td>clear_origin('L_*temp)</td></tr> <tr><td>clear_origin('temp)</td></tr> <tr><td>introduce_subset('v, 'temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] c -> d d1 [ label = <<table border="0"> <tr><td>d1: temp = v</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('v)</td></tr> <tr><td>clear_origin('L_*temp)</td></tr> <tr><td>clear_origin('temp)</td></tr> <tr><td>introduce_subset('v, 'temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] c1 -> d1 d2 [ label = <<table border="0"> <tr><td>d2: temp = v</td></tr> <tr><td>-------------------</td></tr> <tr><td>access_origin('v)</td></tr> <tr><td>clear_origin('L_*temp)</td></tr> <tr><td>clear_origin('temp)</td></tr> <tr><td>introduce_subset('v, 'temp)</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] c2 -> d2 e [ label = <<table border="0"> <tr><td>e: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] c -> e e1 [ label = <<table border="0"> <tr><td>e1: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] c1 -> e1 e2 [ label = <<table border="0"> <tr><td>e2: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] c2 -> e2 f [ label = <<table border="0"> <tr><td>f: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_*temp, 'L_*temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'temp)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'L_*temp)</td></tr> <tr><td>subset('t0, 't0)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] g -> f h -> f f1 [ label = <<table border="0"> <tr><td>f1: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 'L_*temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'temp)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'L_*temp)</td></tr> <tr><td>subset('t0, 't0)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] g1 -> f1 h1 -> f1 f2 [ label = <<table border="0"> <tr><td>f2: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 'L_*temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'temp)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'L_*temp)</td></tr> <tr><td>subset('t0, 't0)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> <tr><td>subset('v, 'L_*temp)</td></tr> <tr><td>subset('v, 't0)</td></tr> <tr><td>subset('v, 'temp)</td></tr> <tr><td>subset('v, 'v)</td></tr> </table>> ] g2 -> f2 h2 -> f2 g [ label = <<table border="0"> <tr><td>g: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('v, 'temp)</td></tr> </table>> ] d -> g g1 [ label = <<table border="0"> <tr><td>g1: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('v, 'temp)</td></tr> </table>> ] d1 -> g1 g2 [ label = <<table border="0"> <tr><td>g2: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'temp)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('v, 'temp)</td></tr> </table>> ] d2 -> g2 h [ label = <<table border="0"> <tr><td>h: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] e -> h h1 [ label = <<table border="0"> <tr><td>h1: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] e1 -> h1 h2 [ label = <<table border="0"> <tr><td>h2: (pass)</td></tr> <tr><td>-------------------</td></tr> <tr><td>-------------------</td></tr> <tr><td>origin_invalidated('temp)</td></tr> <tr><td>subset('L_*temp, 't0)</td></tr> <tr><td>subset('L_*temp, 'v)</td></tr> <tr><td>subset('L_Thing, 'L_*temp)</td></tr> <tr><td>subset('L_Thing, 't0)</td></tr> <tr><td>subset('L_Thing, 'temp)</td></tr> <tr><td>subset('L_Thing, 'v)</td></tr> <tr><td>subset('t0, 'v)</td></tr> <tr><td>subset('temp, 'L_*temp)</td></tr> <tr><td>subset('temp, 't0)</td></tr> <tr><td>subset('temp, 'temp)</td></tr> <tr><td>subset('temp, 'v)</td></tr> </table>> ] e2 -> h2 } ``` ## Niko's concern ``` A -> B A -> C where A has subsets: o0 <= o1 o2 <= o3 A introduces o1 <= o2 o1, o2 are live in B o1, o2 are dead in C ``` Shouldn't we have `o0 <= o3` in C? I don't think we will. Is another way to phrase this `subset_on_entry` vs `subset_on_exit`? I think so. In that case, it makes sense that we: * Apply kills/liveness when transitioning from entry => exit * Apply liveness when we transition exit => entry (along cfg edges) * But never apply both Or rather ``` exit = entry - cleared + introduced live ``` but we could also reduce this to *just* compute subset on entry, couldn't we? ``` subset_on_exit(O1, O2, Node) = subset_on_entry(O1, O2, Node), !clear_origin(O1, Node), !clear_origin(O2, Node). subset_on_exit(O1, O2, Node) = introduce_subset(O1, O2, Node). subset_on_exit(O1, O3, Node) = subset_on_exit(O1, O2, Node). subset_on_exit(O2, O3, Node). subset_on_entry(O1, O2, Node) = cfg_edge(Node_pred, Node), subset_on_exit(O1, O2, Node_pred), ( is_loan_origin(O1); origin_live_on_entry(O1, Node), origin_live_on_entry(O2, Node) ). origin_live_on_entry(O, Node) :- access_origin(O, Node). origin_live_on_entry(O, Node) :- cfg_edge(Node, Node_succ), origin_live_on_entry(O, Node_succ), !clear_origin(O, Node). ``` --- // * First we perform any accesses `access_origin(O, N)` // * Then we invalidate any origins `invalidate_origin(L, N)` // * Then we clear any origins `clear_origin` // * Then we introduce any subsets `introduce_subset` --- Why this matters * On entry: * O1 <= O2 * O2 <= O3 * Clear origins: * O3 * Introduced: * O3 <= O4 * Result: * O1 <= O2 * O3 <= O4 ``` // vec-temp // let mut x = 22; // let mut v = vec![]; // let p = &x; // let mut tmp = &mut v; // Vec::push(tmp, p); // x = 23; // Vec::len(v); // ERROR ```