Skip to content

Commit d4bd9f8

Browse files
committed
add cursor-like example that works
This is an example similar to the linked-list cursor examples where the alpha shows the same imprecision as NLLs, but that can work due to the loans not being live after the loop, or the constraint graph being simple enough that the cfg/subset relationships are the same for reachability and liveness.
1 parent eba66ec commit d4bd9f8

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0499]: cannot borrow `self.buf_read` as mutable more than once at a time
2+
--> $DIR/iterating-updating-mutref.rs:61:23
3+
|
4+
LL | pub fn next<'a>(&'a mut self) -> &'a str {
5+
| -- lifetime `'a` defined here
6+
LL | loop {
7+
LL | let buf = self.buf_read.fill_buf();
8+
| ^^^^^^^^^^^^^ `self.buf_read` was mutably borrowed here in the previous iteration of the loop
9+
LL | if let Some(s) = decode(buf) {
10+
LL | return s;
11+
| - returning this value requires that `self.buf_read` is borrowed for `'a`
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0499`.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// These are some examples of iterating through and updating a mutable ref, similar in spirit to the
2+
// linked-list-like pattern of #46859/#48001 where the polonius alpha analysis shows imprecision,
3+
// unlike the datalog implementation.
4+
//
5+
// They differ in that after the loans prior to the loop are either not live after the loop, or with
6+
// control flow and outlives relationships that are simple enough for the reachability
7+
// approximation. They're thus accepted by the alpha analysis, like NLLs did for the simplest cases
8+
// of flow-sensitivity.
9+
10+
//@ ignore-compare-mode-polonius (explicit revisions)
11+
//@ revisions: nll polonius legacy
12+
//@ [nll] known-bug: #46859
13+
//@ [polonius] check-pass
14+
//@ [polonius] compile-flags: -Z polonius=next
15+
//@ [legacy] check-pass
16+
//@ [legacy] compile-flags: -Z polonius=legacy
17+
18+
// The #46859 OP
19+
struct List<T> {
20+
value: T,
21+
next: Option<Box<List<T>>>,
22+
}
23+
24+
fn to_refs<T>(mut list: &mut List<T>) -> Vec<&mut T> {
25+
let mut result = vec![];
26+
loop {
27+
result.push(&mut list.value);
28+
if let Some(n) = list.next.as_mut() {
29+
list = n;
30+
} else {
31+
return result;
32+
}
33+
}
34+
}
35+
36+
// A similar construction, where paths in the constraint graph are also clearly terminating, so it's
37+
// fine even for NLLs.
38+
fn to_refs2<T>(mut list: &mut List<T>) -> Vec<&mut T> {
39+
let mut result = vec![];
40+
loop {
41+
result.push(&mut list.value);
42+
if let Some(n) = list.next.as_mut() {
43+
list = n;
44+
} else {
45+
break;
46+
}
47+
}
48+
49+
result
50+
}
51+
52+
// Another MCVE from the same issue, but was rejected by NLLs.
53+
pub struct Decoder {
54+
buf_read: BufRead,
55+
}
56+
57+
impl Decoder {
58+
// NLLs fail here
59+
pub fn next<'a>(&'a mut self) -> &'a str {
60+
loop {
61+
let buf = self.buf_read.fill_buf();
62+
if let Some(s) = decode(buf) {
63+
return s;
64+
}
65+
// loop to get more input data
66+
67+
// At this point `buf` is not used anymore.
68+
// With NLL I would expect the borrow to end here,
69+
// such that `self.buf_read` is not borrowed anymore
70+
// by the time we start the next loop iteration.
71+
}
72+
}
73+
}
74+
75+
struct BufRead;
76+
77+
impl BufRead {
78+
fn fill_buf(&mut self) -> &[u8] {
79+
unimplemented!()
80+
}
81+
}
82+
83+
fn decode(_: &[u8]) -> Option<&str> {
84+
unimplemented!()
85+
}
86+
87+
fn main() {}

0 commit comments

Comments
 (0)