Skip to content

Commit cae623c

Browse files
committed
Auto merge of rust-lang#57099 - davidtwco:issue-57098, r=nikomatsakis
NLL: Add closure cannot be moved note. Fixes rust-lang#57098. This PR extends existing logic for checking whether a closure that is `FnOnce` and therefore moves variables that it captures from the environment has already been invoked when being invoked again. Now, this logic will also check whether the closure is being moved after previously being moved or invoked and add an appropriate note. r? @pnkfelix
2 parents 2fba17f + fcad209 commit cae623c

File tree

2 files changed

+49
-39
lines changed

2 files changed

+49
-39
lines changed

src/librustc_mir/borrow_check/error_reporting.rs

+43-39
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
123123
Origin::Mir,
124124
);
125125

126-
self.add_closure_invoked_twice_with_moved_variable_suggestion(
126+
self.add_moved_or_invoked_closure_note(
127127
context.loc,
128128
used_place,
129129
&mut err,
@@ -1329,7 +1329,8 @@ enum StorageDeadOrDrop<'tcx> {
13291329

13301330
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13311331

1332-
/// Adds a suggestion when a closure is invoked twice with a moved variable.
1332+
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
1333+
/// is moved after being invoked.
13331334
///
13341335
/// ```text
13351336
/// note: closure cannot be invoked more than once because it moves the variable `dict` out of
@@ -1339,30 +1340,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13391340
/// LL | for (key, value) in dict {
13401341
/// | ^^^^
13411342
/// ```
1342-
pub(super) fn add_closure_invoked_twice_with_moved_variable_suggestion(
1343+
pub(super) fn add_moved_or_invoked_closure_note(
13431344
&self,
13441345
location: Location,
13451346
place: &Place<'tcx>,
13461347
diag: &mut DiagnosticBuilder<'_>,
13471348
) {
1349+
debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
13481350
let mut target = place.local();
1349-
debug!(
1350-
"add_closure_invoked_twice_with_moved_variable_suggestion: location={:?} place={:?} \
1351-
target={:?}",
1352-
location, place, target,
1353-
);
13541351
for stmt in &self.mir[location.block].statements[location.statement_index..] {
1355-
debug!(
1356-
"add_closure_invoked_twice_with_moved_variable_suggestion: stmt={:?} \
1357-
target={:?}",
1358-
stmt, target,
1359-
);
1352+
debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
13601353
if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind {
1361-
debug!(
1362-
"add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \
1363-
from={:?}",
1364-
into, from,
1365-
);
1354+
debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
13661355
match from {
13671356
Operand::Copy(ref place) |
13681357
Operand::Move(ref place) if target == place.local() =>
@@ -1372,12 +1361,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13721361
}
13731362
}
13741363

1375-
1364+
// Check if we are attempting to call a closure after it has been invoked.
13761365
let terminator = self.mir[location.block].terminator();
1377-
debug!(
1378-
"add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}",
1379-
terminator,
1380-
);
1366+
debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
13811367
if let TerminatorKind::Call {
13821368
func: Operand::Constant(box Constant {
13831369
literal: ty::LazyConst::Evaluated(ty::Const {
@@ -1389,41 +1375,59 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13891375
args,
13901376
..
13911377
} = &terminator.kind {
1392-
debug!("add_closure_invoked_twice_with_moved_variable_suggestion: id={:?}", id);
1378+
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
13931379
if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
13941380
let closure = match args.first() {
13951381
Some(Operand::Copy(ref place)) |
13961382
Some(Operand::Move(ref place)) if target == place.local() =>
13971383
place.local().unwrap(),
13981384
_ => return,
13991385
};
1400-
debug!(
1401-
"add_closure_invoked_twice_with_moved_variable_suggestion: closure={:?}",
1402-
closure,
1403-
);
14041386

1405-
if let ty::TyKind::Closure(did, _substs) = self.mir.local_decls[closure].ty.sty {
1406-
let node_id = match self.infcx.tcx.hir().as_local_node_id(did) {
1407-
Some(node_id) => node_id,
1408-
_ => return,
1409-
};
1387+
debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
1388+
if let ty::TyKind::Closure(did, _) = self.mir.local_decls[closure].ty.sty {
1389+
let node_id = self.infcx.tcx.hir().as_local_node_id(did).unwrap();
14101390
let hir_id = self.infcx.tcx.hir().node_to_hir_id(node_id);
14111391

1412-
if let Some((
1413-
span, name
1414-
)) = self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id) {
1392+
if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
1393+
.closure_kind_origins()
1394+
.get(hir_id)
1395+
{
14151396
diag.span_note(
14161397
*span,
14171398
&format!(
1418-
"closure cannot be invoked more than once because it \
1419-
moves the variable `{}` out of its environment",
1420-
name,
1399+
"closure cannot be invoked more than once because it moves the \
1400+
variable `{}` out of its environment",
1401+
name,
14211402
),
14221403
);
1404+
return;
14231405
}
14241406
}
14251407
}
14261408
}
1409+
1410+
// Check if we are just moving a closure after it has been invoked.
1411+
if let Some(target) = target {
1412+
if let ty::TyKind::Closure(did, _) = self.mir.local_decls[target].ty.sty {
1413+
let node_id = self.infcx.tcx.hir().as_local_node_id(did).unwrap();
1414+
let hir_id = self.infcx.tcx.hir().node_to_hir_id(node_id);
1415+
1416+
if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
1417+
.closure_kind_origins()
1418+
.get(hir_id)
1419+
{
1420+
diag.span_note(
1421+
*span,
1422+
&format!(
1423+
"closure cannot be moved more than once as it is not `Copy` due to \
1424+
moving the variable `{}` out of its environment",
1425+
name
1426+
),
1427+
);
1428+
}
1429+
}
1430+
}
14271431
}
14281432

14291433
/// End-user visible description of `place` if one can be found. If the

src/test/ui/not-copy-closure.nll.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | let b = hello;
55
| ----- value moved here
66
LL | let c = hello; //~ ERROR use of moved value: `hello` [E0382]
77
| ^^^^^ value used here after move
8+
|
9+
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `a` out of its environment
10+
--> $DIR/not-copy-closure.rs:6:9
11+
|
12+
LL | a += 1;
13+
| ^
814

915
error: aborting due to previous error
1016

0 commit comments

Comments
 (0)