Skip to content

Commit 9b2b299

Browse files
Rollup merge of #154674 - Enselic:unused-outlive, r=jdonszelmann
borrowck: Don't mention unused vars in closure outlive errors When there is a closure arg lifetime error, the code does its best to find variables that correspond to the lifetimes involved. This commit stops mentioning arguments that are unused in closures, since they are not relevant. This removes the "red herring" of #113121. The `user_arg_index` nomenclature can be applied more broadly, but then the diff becomes annoyingly big, so I chose not to do that. Review each commit individually for a nicer experience.
2 parents 6179c56 + 94ee70e commit 9b2b299

3 files changed

Lines changed: 66 additions & 5 deletions

File tree

compiler/rustc_borrowck/src/diagnostics/var_name.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use rustc_index::IndexSlice;
2-
use rustc_middle::mir::{Body, Local};
2+
use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor};
3+
use rustc_middle::mir::{Body, Local, Place};
34
use rustc_middle::ty::{self, RegionVid, TyCtxt};
45
use rustc_span::{Span, Symbol};
56
use tracing::debug;
67

78
use crate::region_infer::RegionInferenceContext;
89

910
impl<'tcx> RegionInferenceContext<'tcx> {
11+
/// Find the the name and span of the variable corresponding to the given region.
12+
/// The returned var will also be ensured to actually be used in `body`.
1013
pub(crate) fn get_var_name_and_span_for_region(
1114
&self,
1215
tcx: TyCtxt<'tcx>,
@@ -22,13 +25,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
2225
self.get_upvar_index_for_region(tcx, fr)
2326
.map(|index| {
2427
// FIXME(project-rfc-2229#8): Use place span for diagnostics
28+
// We know our upvars are used thanks to `fn compute_min_captures()` in `upvar.rs`.
2529
let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index);
2630
(Some(name), span)
2731
})
2832
.or_else(|| {
2933
debug!("get_var_name_and_span_for_region: attempting argument");
30-
self.get_argument_index_for_region(tcx, fr).map(|index| {
31-
self.get_argument_name_and_span_for_region(body, local_names, index)
34+
self.get_argument_index_for_region(tcx, fr).and_then(|index| {
35+
let local = self.user_arg_index_to_local(body, index);
36+
if body_uses_local(body, local) {
37+
Some(self.get_argument_name_and_span_for_region(body, local_names, index))
38+
} else {
39+
debug!("get_var_name_and_span_for_region: skipping unused local {local:?}");
40+
None
41+
}
3242
})
3343
})
3444
}
@@ -105,6 +115,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
105115
Some(argument_index)
106116
}
107117

118+
/// Given the index of an argument as seen from the user (i.e. excluding
119+
/// implicit inputs), returns the corresponding MIR local.
120+
fn user_arg_index_to_local(&self, body: &Body<'tcx>, user_arg_index: usize) -> Local {
121+
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
122+
body.args_iter().nth(implicit_inputs + user_arg_index).unwrap()
123+
}
124+
108125
/// Given the index of an argument, finds its name (if any) and the span from where it was
109126
/// declared.
110127
pub(crate) fn get_argument_name_and_span_for_region(
@@ -113,8 +130,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
113130
local_names: &IndexSlice<Local, Option<Symbol>>,
114131
argument_index: usize,
115132
) -> (Option<Symbol>, Span) {
116-
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
117-
let argument_local = Local::from_usize(implicit_inputs + argument_index + 1);
133+
let argument_local = self.user_arg_index_to_local(body, argument_index);
118134
debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}");
119135

120136
let argument_name = local_names[argument_local];
@@ -126,3 +142,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
126142
(argument_name, argument_span)
127143
}
128144
}
145+
146+
fn body_uses_local<'tcx>(body: &Body<'tcx>, target: Local) -> bool {
147+
let mut used = false;
148+
VisitPlacesWith(|place: Place<'_>, context: PlaceContext| {
149+
if !matches!(context, PlaceContext::NonUse(_)) && place.local == target {
150+
used = true;
151+
}
152+
})
153+
.visit_body(body);
154+
used
155+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//! Regression test for <https://github.com/rust-lang/rust/issues/113121>.
2+
3+
#![allow(unused_variables)]
4+
5+
fn consume<T: 'static>(_: T) {}
6+
7+
fn foo<'a>(
8+
used_arg: &'a u8,
9+
unused_arg: &'static u16, // Unused in closure. Must not appear in error.
10+
) {
11+
let unused_var: &'static u32 = &42; // Unused in closure. Must not appear in error.
12+
13+
let c = move || used_arg;
14+
consume(c); //~ ERROR: borrowed data escapes outside of function
15+
}
16+
17+
fn main() {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0521]: borrowed data escapes outside of function
2+
--> $DIR/var-matching-lifetime-but-unused-not-mentioned.rs:14:5
3+
|
4+
LL | fn foo<'a>(
5+
| -- lifetime `'a` defined here
6+
LL | used_arg: &'a u8,
7+
| -------- `used_arg` is a reference that is only valid in the function body
8+
...
9+
LL | consume(c);
10+
| ^^^^^^^^^^
11+
| |
12+
| `used_arg` escapes the function body here
13+
| argument requires that `'a` must outlive `'static`
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0521`.

0 commit comments

Comments
 (0)