Skip to content

Commit b98d127

Browse files
authored
Rollup merge of #147974 - JohnTitor:diag-detect-buf-reuse-pattern, r=estebank
Improve diagnostics for buffer reuse with borrowed references Addresses #147694 I'm not sure the current note wording is the best so I appreciate any feedback.
2 parents 203130b + 61b26f9 commit b98d127

File tree

7 files changed

+185
-5
lines changed

7 files changed

+185
-5
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3087,6 +3087,39 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
30873087
});
30883088

30893089
explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);
3090+
3091+
// Detect buffer reuse pattern
3092+
if let BorrowExplanation::UsedLater(_dropped_local, _, _, _) = explanation {
3093+
// Check all locals at the borrow location to find Vec<&T> types
3094+
for (local, local_decl) in self.body.local_decls.iter_enumerated() {
3095+
if let ty::Adt(adt_def, args) = local_decl.ty.kind()
3096+
&& self.infcx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())
3097+
&& args.len() > 0
3098+
{
3099+
let vec_inner_ty = args.type_at(0);
3100+
// Check if Vec contains references
3101+
if vec_inner_ty.is_ref() {
3102+
let local_place = local.into();
3103+
if let Some(local_name) = self.describe_place(local_place) {
3104+
err.span_label(
3105+
local_decl.source_info.span,
3106+
format!("variable `{local_name}` declared here"),
3107+
);
3108+
err.note(
3109+
format!(
3110+
"`{local_name}` is a collection that stores borrowed references, \
3111+
but {name} does not live long enough to be stored in it"
3112+
)
3113+
);
3114+
err.help(
3115+
"buffer reuse with borrowed references requires unsafe code or restructuring"
3116+
);
3117+
break;
3118+
}
3119+
}
3120+
}
3121+
}
3122+
}
30903123
}
30913124

30923125
err
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
fn process_data(_: &[&[u8]]) {}
2+
3+
fn test_buffer_cleared_after_use() {
4+
let sources = vec![vec![1u8, 2, 3, 4, 5], vec![6, 7, 8, 9]];
5+
let mut buffer: Vec<&[u8]> = vec![];
6+
//~^ NOTE variable `buffer` declared here
7+
8+
for source in sources {
9+
let data: Vec<u8> = source;
10+
//~^ NOTE binding `data` declared here
11+
buffer.extend(data.split(|x| *x == 3));
12+
//~^ ERROR `data` does not live long enough
13+
//~| NOTE borrowed value does not live long enough
14+
//~| NOTE borrow later used here
15+
//~| NOTE `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
16+
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
17+
process_data(&buffer);
18+
buffer.clear();
19+
} //~ NOTE `data` dropped here while still borrowed
20+
}
21+
22+
fn test_buffer_cleared_at_start() {
23+
let sources = vec![vec![1u8, 2, 3, 4, 5], vec![6, 7, 8, 9]];
24+
let mut buffer: Vec<&[u8]> = vec![];
25+
//~^ NOTE variable `buffer` declared here
26+
27+
for source in sources {
28+
buffer.clear();
29+
//~^ NOTE borrow later used here
30+
let data: Vec<u8> = source;
31+
//~^ NOTE binding `data` declared here
32+
buffer.extend(data.split(|x| *x == 3));
33+
//~^ ERROR `data` does not live long enough
34+
//~| NOTE borrowed value does not live long enough
35+
//~| NOTE `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
36+
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
37+
process_data(&buffer);
38+
} //~ NOTE `data` dropped here while still borrowed
39+
}
40+
41+
fn test_no_explicit_clear() {
42+
let sources = vec![vec![1u8, 2, 3, 4, 5], vec![6, 7, 8, 9]];
43+
let mut buffer: Vec<&[u8]> = vec![];
44+
//~^ NOTE variable `buffer` declared here
45+
46+
for source in sources {
47+
let data: Vec<u8> = source;
48+
//~^ NOTE binding `data` declared here
49+
buffer.extend(data.split(|x| *x == 3));
50+
//~^ ERROR `data` does not live long enough
51+
//~| NOTE borrowed value does not live long enough
52+
//~| NOTE borrow later used here
53+
//~| NOTE `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
54+
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
55+
process_data(&buffer);
56+
} //~ NOTE `data` dropped here while still borrowed
57+
}
58+
59+
fn main() {
60+
test_buffer_cleared_after_use();
61+
test_buffer_cleared_at_start();
62+
test_no_explicit_clear();
63+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error[E0597]: `data` does not live long enough
2+
--> $DIR/buffer-reuse-pattern-issue-147694.rs:11:23
3+
|
4+
LL | let mut buffer: Vec<&[u8]> = vec![];
5+
| ---------- variable `buffer` declared here
6+
...
7+
LL | let data: Vec<u8> = source;
8+
| ---- binding `data` declared here
9+
LL |
10+
LL | buffer.extend(data.split(|x| *x == 3));
11+
| ------ ^^^^ borrowed value does not live long enough
12+
| |
13+
| borrow later used here
14+
...
15+
LL | }
16+
| - `data` dropped here while still borrowed
17+
|
18+
= note: `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
19+
= help: buffer reuse with borrowed references requires unsafe code or restructuring
20+
21+
error[E0597]: `data` does not live long enough
22+
--> $DIR/buffer-reuse-pattern-issue-147694.rs:32:23
23+
|
24+
LL | let mut buffer: Vec<&[u8]> = vec![];
25+
| ---------- variable `buffer` declared here
26+
...
27+
LL | buffer.clear();
28+
| ------ borrow later used here
29+
LL |
30+
LL | let data: Vec<u8> = source;
31+
| ---- binding `data` declared here
32+
LL |
33+
LL | buffer.extend(data.split(|x| *x == 3));
34+
| ^^^^ borrowed value does not live long enough
35+
...
36+
LL | }
37+
| - `data` dropped here while still borrowed
38+
|
39+
= note: `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
40+
= help: buffer reuse with borrowed references requires unsafe code or restructuring
41+
42+
error[E0597]: `data` does not live long enough
43+
--> $DIR/buffer-reuse-pattern-issue-147694.rs:49:23
44+
|
45+
LL | let mut buffer: Vec<&[u8]> = vec![];
46+
| ---------- variable `buffer` declared here
47+
...
48+
LL | let data: Vec<u8> = source;
49+
| ---- binding `data` declared here
50+
LL |
51+
LL | buffer.extend(data.split(|x| *x == 3));
52+
| ------ ^^^^ borrowed value does not live long enough
53+
| |
54+
| borrow later used here
55+
...
56+
LL | }
57+
| - `data` dropped here while still borrowed
58+
|
59+
= note: `buffer` is a collection that stores borrowed references, but `data` does not live long enough to be stored in it
60+
= help: buffer reuse with borrowed references requires unsafe code or restructuring
61+
62+
error: aborting due to 3 previous errors
63+
64+
For more information about this error, try `rustc --explain E0597`.

tests/ui/issues/issue-52126-assign-op-invariance.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ error[E0597]: `line` does not live long enough
44
LL | for line in vec!["123456789".to_string(), "12345678".to_string()] {
55
| ---- binding `line` declared here
66
LL | let v: Vec<&str> = line.split_whitespace().collect();
7-
| ^^^^ borrowed value does not live long enough
7+
| - ^^^^ borrowed value does not live long enough
8+
| |
9+
| variable `v` declared here
810
...
911
LL | acc += cnt2;
1012
| --- borrow later used here
1113
...
1214
LL | }
1315
| - `line` dropped here while still borrowed
16+
|
17+
= note: `v` is a collection that stores borrowed references, but `line` does not live long enough to be stored in it
18+
= help: buffer reuse with borrowed references requires unsafe code or restructuring
1419

1520
error: aborting due to 1 previous error
1621

tests/ui/span/borrowck-let-suggestion-suffixes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ fn id<T>(x: T) -> T { x }
33
fn f() {
44
let old = ['o']; // statement 0
55
let mut v1 = Vec::new(); // statement 1
6+
//~^ NOTE variable `v1` declared here
67

78
let mut v2 = Vec::new(); // statement 2
89

@@ -13,6 +14,8 @@ fn f() {
1314
v2.push(&young[0]); // statement 4
1415
//~^ ERROR `young[_]` does not live long enough
1516
//~| NOTE borrowed value does not live long enough
17+
//~| NOTE `v1` is a collection that stores borrowed references, but `young[_]` does not live long enough to be stored in it
18+
//~| HELP buffer reuse with borrowed references requires unsafe code or restructuring
1619
} //~ NOTE `young[_]` dropped here while still borrowed
1720

1821
let mut v3 = Vec::new(); // statement 5

tests/ui/span/borrowck-let-suggestion-suffixes.stderr

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
error[E0597]: `young[_]` does not live long enough
2-
--> $DIR/borrowck-let-suggestion-suffixes.rs:13:17
2+
--> $DIR/borrowck-let-suggestion-suffixes.rs:14:17
33
|
4+
LL | let mut v1 = Vec::new(); // statement 1
5+
| ------ variable `v1` declared here
6+
...
47
LL | let young = ['y']; // statement 3
58
| ----- binding `young` declared here
69
...
@@ -12,9 +15,12 @@ LL | }
1215
...
1316
LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref();
1417
| -- borrow later used here
18+
|
19+
= note: `v1` is a collection that stores borrowed references, but `young[_]` does not live long enough to be stored in it
20+
= help: buffer reuse with borrowed references requires unsafe code or restructuring
1521

1622
error[E0716]: temporary value dropped while borrowed
17-
--> $DIR/borrowck-let-suggestion-suffixes.rs:20:14
23+
--> $DIR/borrowck-let-suggestion-suffixes.rs:23:14
1824
|
1925
LL | v3.push(&id('x')); // statement 6
2026
| ^^^^^^^ - temporary value is freed at the end of this statement
@@ -31,7 +37,7 @@ LL ~ v3.push(&binding); // statement 6
3137
|
3238

3339
error[E0716]: temporary value dropped while borrowed
34-
--> $DIR/borrowck-let-suggestion-suffixes.rs:30:18
40+
--> $DIR/borrowck-let-suggestion-suffixes.rs:33:18
3541
|
3642
LL | v4.push(&id('y'));
3743
| ^^^^^^^ - temporary value is freed at the end of this statement
@@ -44,7 +50,7 @@ LL | v4.use_ref();
4450
= note: consider using a `let` binding to create a longer lived value
4551

4652
error[E0716]: temporary value dropped while borrowed
47-
--> $DIR/borrowck-let-suggestion-suffixes.rs:41:14
53+
--> $DIR/borrowck-let-suggestion-suffixes.rs:44:14
4854
|
4955
LL | v5.push(&id('z'));
5056
| ^^^^^^^ - temporary value is freed at the end of this statement

tests/ui/span/regions-escape-loop-via-vec.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ LL | _y.push(&mut z);
2323
error[E0597]: `z` does not live long enough
2424
--> $DIR/regions-escape-loop-via-vec.rs:7:17
2525
|
26+
LL | let mut _y = vec![&mut x];
27+
| ------ variable `_y` declared here
28+
LL | while x < 10 {
2629
LL | let mut z = x;
2730
| ----- binding `z` declared here
2831
LL | _y.push(&mut z);
@@ -32,6 +35,9 @@ LL | _y.push(&mut z);
3235
...
3336
LL | }
3437
| - `z` dropped here while still borrowed
38+
|
39+
= note: `_y` is a collection that stores borrowed references, but `z` does not live long enough to be stored in it
40+
= help: buffer reuse with borrowed references requires unsafe code or restructuring
3541

3642
error[E0503]: cannot use `x` because it was mutably borrowed
3743
--> $DIR/regions-escape-loop-via-vec.rs:9:9

0 commit comments

Comments
 (0)