Skip to content

Commit 132f5fc

Browse files
committed
Auto merge of #75933 - Aaron1011:feature/closure-move-err, r=oli-obk
Point to a move-related span when pointing to closure upvars Fixes #75904 When emitting move/borrow errors, we may point into a closure to indicate why an upvar is used in the closure. However, we use the 'upvar span', which is just an arbitrary usage of the upvar. If the upvar is used in multiple places (e.g. a borrow and a move), we may end up pointing to the borrow. If the overall error is a move error, this can be confusing. This PR tracks the span that caused an upvar to become captured by-value instead of by-ref (assuming that it's not a `move` closure). We use this span instead of the 'upvar' span when we need to point to an upvar usage during borrow checking.
2 parents 3d0c847 + b5b8b93 commit 132f5fc

File tree

12 files changed

+102
-18
lines changed

12 files changed

+102
-18
lines changed

src/librustc_middle/ty/mod.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,13 @@ pub enum UpvarCapture<'tcx> {
721721
/// Upvar is captured by value. This is always true when the
722722
/// closure is labeled `move`, but can also be true in other cases
723723
/// depending on inference.
724-
ByValue,
724+
///
725+
/// If the upvar was inferred to be captured by value (e.g. `move`
726+
/// was not used), then the `Span` points to a usage that
727+
/// required it. There may be more than one such usage
728+
/// (e.g. `|| { a; a; }`), in which case we pick an
729+
/// arbitrary one.
730+
ByValue(Option<Span>),
725731

726732
/// Upvar is captured by reference.
727733
ByRef(UpvarBorrow<'tcx>),

src/librustc_mir/borrow_check/diagnostics/mod.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -938,19 +938,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
938938
"closure_span: def_id={:?} target_place={:?} places={:?}",
939939
def_id, target_place, places
940940
);
941-
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(def_id.as_local()?);
941+
let local_did = def_id.as_local()?;
942+
let hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(local_did);
942943
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
943944
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
944945
if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
945-
for (upvar, place) in self.infcx.tcx.upvars_mentioned(def_id)?.values().zip(places) {
946+
for ((upvar_hir_id, upvar), place) in
947+
self.infcx.tcx.upvars_mentioned(def_id)?.iter().zip(places)
948+
{
946949
match place {
947950
Operand::Copy(place) | Operand::Move(place)
948951
if target_place == place.as_ref() =>
949952
{
950953
debug!("closure_span: found captured local {:?}", place);
951954
let body = self.infcx.tcx.hir().body(*body_id);
952955
let generator_kind = body.generator_kind();
953-
return Some((*args_span, generator_kind, upvar.span));
956+
let upvar_id = ty::UpvarId {
957+
var_path: ty::UpvarPath { hir_id: *upvar_hir_id },
958+
closure_expr_id: local_did,
959+
};
960+
961+
// If we have a more specific span available, point to that.
962+
// We do this even though this span might be part of a borrow error
963+
// message rather than a move error message. Our goal is to point
964+
// to a span that shows why the upvar is used in the closure,
965+
// so a move-related span is as good as any (and potentially better,
966+
// if the overall error is due to a move of the upvar).
967+
let usage_span =
968+
match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) {
969+
ty::UpvarCapture::ByValue(Some(span)) => span,
970+
_ => upvar.span,
971+
};
972+
return Some((*args_span, generator_kind, usage_span));
954973
}
955974
_ => {}
956975
}

src/librustc_mir/borrow_check/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ fn do_mir_borrowck<'a, 'tcx>(
163163
let var_hir_id = upvar_id.var_path.hir_id;
164164
let capture = tables.upvar_capture(*upvar_id);
165165
let by_ref = match capture {
166-
ty::UpvarCapture::ByValue => false,
166+
ty::UpvarCapture::ByValue(_) => false,
167167
ty::UpvarCapture::ByRef(..) => true,
168168
};
169169
let mut upvar = Upvar {

src/librustc_mir_build/build/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
876876
let mut projs = closure_env_projs.clone();
877877
projs.push(ProjectionElem::Field(Field::new(i), ty));
878878
match capture {
879-
ty::UpvarCapture::ByValue => {}
879+
ty::UpvarCapture::ByValue(_) => {}
880880
ty::UpvarCapture::ByRef(..) => {
881881
projs.push(ProjectionElem::Deref);
882882
}

src/librustc_mir_build/thir/cx/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ fn convert_var<'tcx>(
959959
// ...but the upvar might be an `&T` or `&mut T` capture, at which
960960
// point we need an implicit deref
961961
match cx.typeck_results().upvar_capture(upvar_id) {
962-
ty::UpvarCapture::ByValue => field_kind,
962+
ty::UpvarCapture::ByValue(_) => field_kind,
963963
ty::UpvarCapture::ByRef(borrow) => ExprKind::Deref {
964964
arg: Expr {
965965
temp_lifetime,
@@ -1074,7 +1074,7 @@ fn capture_upvar<'tcx>(
10741074
kind: convert_var(cx, closure_expr, var_hir_id),
10751075
};
10761076
match upvar_capture {
1077-
ty::UpvarCapture::ByValue => captured_var.to_ref(),
1077+
ty::UpvarCapture::ByValue(_) => captured_var.to_ref(),
10781078
ty::UpvarCapture::ByRef(upvar_borrow) => {
10791079
let borrow_kind = match upvar_borrow.kind {
10801080
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,

src/librustc_passes/liveness.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
945945
let var = self.variable(var_hir_id, upvar.span);
946946
self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE);
947947
}
948-
ty::UpvarCapture::ByValue => {}
948+
ty::UpvarCapture::ByValue(_) => {}
949949
}
950950
}
951951
}
@@ -1610,7 +1610,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
16101610
closure_expr_id: self.ir.body_owner,
16111611
};
16121612
match self.typeck_results.upvar_capture(upvar_id) {
1613-
ty::UpvarCapture::ByValue => {}
1613+
ty::UpvarCapture::ByValue(_) => {}
16141614
ty::UpvarCapture::ByRef(..) => continue,
16151615
};
16161616
if self.used_on_entry(entry_ln, var) {

src/librustc_typeck/check/regionck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
786786
return;
787787
}
788788
}
789-
ty::UpvarCapture::ByValue => {}
789+
ty::UpvarCapture::ByValue(_) => {}
790790
}
791791
let fn_hir_id = self.tcx.hir().local_def_id_to_hir_id(upvar_id.closure_expr_id);
792792
let ty = self.resolve_node_type(fn_hir_id);

src/librustc_typeck/check/upvar.rs

+34-5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use rustc_infer::infer::UpvarRegion;
4242
use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
4343
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
4444
use rustc_span::{Span, Symbol};
45+
use std::collections::hash_map::Entry;
4546

4647
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4748
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
@@ -124,7 +125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
124125
closure_captures.insert(var_hir_id, upvar_id);
125126

126127
let capture_kind = match capture_clause {
127-
hir::CaptureBy::Value => ty::UpvarCapture::ByValue,
128+
hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None),
128129
hir::CaptureBy::Ref => {
129130
let origin = UpvarRegion(upvar_id, span);
130131
let upvar_region = self.next_region_var(origin);
@@ -237,7 +238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
237238
debug!("var_id={:?} upvar_ty={:?} capture={:?}", var_hir_id, upvar_ty, capture);
238239

239240
match capture {
240-
ty::UpvarCapture::ByValue => upvar_ty,
241+
ty::UpvarCapture::ByValue(_) => upvar_ty,
241242
ty::UpvarCapture::ByRef(borrow) => tcx.mk_ref(
242243
borrow.region,
243244
ty::TypeAndMut { ty: upvar_ty, mutbl: borrow.kind.to_mutbl_lossy() },
@@ -300,15 +301,43 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
300301

301302
debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id);
302303

304+
let usage_span = tcx.hir().span(place_with_id.hir_id);
305+
303306
// To move out of an upvar, this must be a FnOnce closure
304307
self.adjust_closure_kind(
305308
upvar_id.closure_expr_id,
306309
ty::ClosureKind::FnOnce,
307-
tcx.hir().span(place_with_id.hir_id),
310+
usage_span,
308311
var_name(tcx, upvar_id.var_path.hir_id),
309312
);
310313

311-
self.adjust_upvar_captures.insert(upvar_id, ty::UpvarCapture::ByValue);
314+
// In a case like `let pat = upvar`, don't use the span
315+
// of the pattern, as this just looks confusing.
316+
let by_value_span = match tcx.hir().get(place_with_id.hir_id) {
317+
hir::Node::Pat(_) => None,
318+
_ => Some(usage_span),
319+
};
320+
321+
let new_capture = ty::UpvarCapture::ByValue(by_value_span);
322+
match self.adjust_upvar_captures.entry(upvar_id) {
323+
Entry::Occupied(mut e) => {
324+
match e.get() {
325+
// We always overwrite `ByRef`, since we require
326+
// that the upvar be available by value.
327+
//
328+
// If we had a previous by-value usage without a specific
329+
// span, use ours instead. Otherwise, keep the first span
330+
// we encountered, since there isn't an obviously better one.
331+
ty::UpvarCapture::ByRef(_) | ty::UpvarCapture::ByValue(None) => {
332+
e.insert(new_capture);
333+
}
334+
_ => {}
335+
}
336+
}
337+
Entry::Vacant(e) => {
338+
e.insert(new_capture);
339+
}
340+
}
312341
}
313342

314343
/// Indicates that `place_with_id` is being directly mutated (e.g., assigned
@@ -404,7 +433,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
404433
);
405434

406435
match upvar_capture {
407-
ty::UpvarCapture::ByValue => {
436+
ty::UpvarCapture::ByValue(_) => {
408437
// Upvar is already by-value, the strongest criteria.
409438
}
410439
ty::UpvarCapture::ByRef(mut upvar_borrow) => {

src/librustc_typeck/check/writeback.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
331331
fn visit_upvar_capture_map(&mut self) {
332332
for (upvar_id, upvar_capture) in self.fcx.typeck_results.borrow().upvar_capture_map.iter() {
333333
let new_upvar_capture = match *upvar_capture {
334-
ty::UpvarCapture::ByValue => ty::UpvarCapture::ByValue,
334+
ty::UpvarCapture::ByValue(span) => ty::UpvarCapture::ByValue(span),
335335
ty::UpvarCapture::ByRef(ref upvar_borrow) => {
336336
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
337337
kind: upvar_borrow.kind,

src/librustc_typeck/expr_use_visitor.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
559559
var_id,
560560
));
561561
match upvar_capture {
562-
ty::UpvarCapture::ByValue => {
562+
ty::UpvarCapture::ByValue(_) => {
563563
let mode = copy_or_move(&self.mc, &captured_place);
564564
self.delegate.consume(&captured_place, mode);
565565
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Regression test for issue #75904
2+
// Tests that we point at an expression
3+
// that required the upvar to be moved, rather than just borrowed.
4+
5+
struct NotCopy;
6+
7+
fn main() {
8+
let mut a = NotCopy;
9+
loop {
10+
|| { //~ ERROR use of moved value
11+
&mut a;
12+
a;
13+
};
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0382]: use of moved value: `a`
2+
--> $DIR/issue-75904-move-closure-loop.rs:10:9
3+
|
4+
LL | let mut a = NotCopy;
5+
| ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait
6+
LL | loop {
7+
LL | || {
8+
| ^^ value moved into closure here, in previous iteration of loop
9+
LL | &mut a;
10+
LL | a;
11+
| - use occurs due to use in closure
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)