Skip to content

Commit fcad209

Browse files
committed
Add closure cannot be moved note.
This commit 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.
1 parent 79d8a0f commit fcad209

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,
@@ -1331,7 +1331,8 @@ enum StorageDeadOrDrop<'tcx> {
13311331

13321332
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13331333

1334-
/// Adds a suggestion when a closure is invoked twice with a moved variable.
1334+
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
1335+
/// is moved after being invoked.
13351336
///
13361337
/// ```text
13371338
/// note: closure cannot be invoked more than once because it moves the variable `dict` out of
@@ -1341,30 +1342,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13411342
/// LL | for (key, value) in dict {
13421343
/// | ^^^^
13431344
/// ```
1344-
pub(super) fn add_closure_invoked_twice_with_moved_variable_suggestion(
1345+
pub(super) fn add_moved_or_invoked_closure_note(
13451346
&self,
13461347
location: Location,
13471348
place: &Place<'tcx>,
13481349
diag: &mut DiagnosticBuilder<'_>,
13491350
) {
1351+
debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
13501352
let mut target = place.local();
1351-
debug!(
1352-
"add_closure_invoked_twice_with_moved_variable_suggestion: location={:?} place={:?} \
1353-
target={:?}",
1354-
location, place, target,
1355-
);
13561353
for stmt in &self.mir[location.block].statements[location.statement_index..] {
1357-
debug!(
1358-
"add_closure_invoked_twice_with_moved_variable_suggestion: stmt={:?} \
1359-
target={:?}",
1360-
stmt, target,
1361-
);
1354+
debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
13621355
if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind {
1363-
debug!(
1364-
"add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \
1365-
from={:?}",
1366-
into, from,
1367-
);
1356+
debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
13681357
match from {
13691358
Operand::Copy(ref place) |
13701359
Operand::Move(ref place) if target == place.local() =>
@@ -1374,12 +1363,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13741363
}
13751364
}
13761365

1377-
1366+
// Check if we are attempting to call a closure after it has been invoked.
13781367
let terminator = self.mir[location.block].terminator();
1379-
debug!(
1380-
"add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}",
1381-
terminator,
1382-
);
1368+
debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
13831369
if let TerminatorKind::Call {
13841370
func: Operand::Constant(box Constant {
13851371
literal: ty::Const { ty: &ty::TyS { sty: ty::TyKind::FnDef(id, _), .. }, .. },
@@ -1388,41 +1374,59 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13881374
args,
13891375
..
13901376
} = &terminator.kind {
1391-
debug!("add_closure_invoked_twice_with_moved_variable_suggestion: id={:?}", id);
1377+
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
13921378
if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
13931379
let closure = match args.first() {
13941380
Some(Operand::Copy(ref place)) |
13951381
Some(Operand::Move(ref place)) if target == place.local() =>
13961382
place.local().unwrap(),
13971383
_ => return,
13981384
};
1399-
debug!(
1400-
"add_closure_invoked_twice_with_moved_variable_suggestion: closure={:?}",
1401-
closure,
1402-
);
14031385

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

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

14281432
/// 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)