Skip to content

Commit 3d44596

Browse files
committed
add cursor-like example that works
These are example that are similar to the linked-list cursor examples where the alpha shows the same imprecision as NLLs, but 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 542646c commit 3d44596

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-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:59: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+
...
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: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
//@ [polonius] check-pass
13+
//@ [polonius] compile-flags: -Z polonius=next
14+
//@ [legacy] check-pass
15+
//@ [legacy] compile-flags: -Z polonius=legacy
16+
17+
// The #46859 OP
18+
struct List<T> {
19+
value: T,
20+
next: Option<Box<List<T>>>,
21+
}
22+
23+
fn to_refs<T>(mut list: &mut List<T>) -> Vec<&mut T> {
24+
let mut result = vec![];
25+
loop {
26+
result.push(&mut list.value);
27+
if let Some(n) = list.next.as_mut() {
28+
list = n;
29+
} else {
30+
return result;
31+
}
32+
}
33+
}
34+
35+
// A similar construction, where paths in the constraint graph are also clearly terminating, so it's
36+
// fine even for NLLs.
37+
fn to_refs2<T>(mut list: &mut List<T>) -> Vec<&mut T> {
38+
let mut result = vec![];
39+
loop {
40+
result.push(&mut list.value);
41+
if let Some(n) = list.next.as_mut() {
42+
list = n;
43+
} else {
44+
break;
45+
}
46+
}
47+
48+
result
49+
}
50+
51+
// Another MCVE from the same issue, but was rejected by NLLs.
52+
pub struct Decoder {
53+
buf_read: BufRead,
54+
}
55+
56+
impl Decoder {
57+
pub fn next<'a>(&'a mut self) -> &'a str {
58+
loop {
59+
let buf = self.buf_read.fill_buf();
60+
//[nll]~^ ERROR cannot borrow `self.buf_read` as mutable more than once at a time
61+
if let Some(s) = decode(buf) {
62+
return s;
63+
}
64+
// loop to get more input data
65+
66+
// At this point `buf` is not used anymore.
67+
// With NLL I would expect the borrow to end here,
68+
// such that `self.buf_read` is not borrowed anymore
69+
// by the time we start the next loop iteration.
70+
}
71+
}
72+
}
73+
74+
struct BufRead;
75+
76+
impl BufRead {
77+
fn fill_buf(&mut self) -> &[u8] {
78+
unimplemented!()
79+
}
80+
}
81+
82+
fn decode(_: &[u8]) -> Option<&str> {
83+
unimplemented!()
84+
}
85+
86+
fn main() {}

0 commit comments

Comments
 (0)