Where Polonius is different from NLL
by H. Haldi and M. Algelly
Report of the 9th of May 2023
/*0*/ let mut x: u32 = 22;
/*1*/ let y: &{L1} u32 = &{L1} x /* Loan L1 */;
/*2*/ x += 1;
/*3*/ print(y);
x has origin L1y has the same origin L1x, so violates the terms of the loan L1y is live on line 2 and its type includes L1
y is &{L1} u32→ ERROR
fn get_or_insert(
map: &mut HashMap<u32, String>,
) -> &String {
match map.get(&22) {
Some(v) => v,
None => {
map.insert(22, String::from("hi"));
&map[&22]
}
}
}
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
--> src/main.rs:55:13
|
50 | map: &mut HashMap<u32, String>,
| - let's call the lifetime of this reference `'1`
51 | ) -> &String {
52 | match map.get(&22) {
| ------------ immutable borrow occurs here
53 | Some(v) => v,
| - returning this value requires that `*map` is borrowed for `'1`
54 | None => {
55 | map.insert(22, String::from("hi"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
fn get_or_insert<'a>(
map: &'a mut HashMap<u32, String>,
) -> &'a String {
match HashMap::get(&*map, &22) { // Loan of the place *map
Some(v) => v,
None => {
map.insert(22, String::from("hi"));
&map[&22]
}
}
}
However, in NLL, the lifetime of this loan is 'a, and not a set of line numbers.
'a), which includes all the lines of the functionfn get_or_insert<'a>(
map: &'a mut HashMap<u32, String>,
) -> &'a String {
match HashMap::get(&*map, &22) { // Loan L1
Some(v) => v,
None => {
map.insert(22, String::from("hi")); // Here
&map[&22]
}
}
}
v has the loan L1 in its typev is not live here→ Mutable borrow on *map is allowed
Let’s define formally, using datalog syntax, how Polonius detects error:
error(P) :-
invalidates(P, L),
loan_live_at(L, P).
where P is a program point (statement) and L is a loan.
P, the point modifies PP, the point accesses PThe rule loan_live_at(L, P) is much more complex.
I.e. If the terms of the loan L are invalidated at point P, then the region is invalidated.
requires relation