Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NLL: Add closure cannot be moved note. #57099

Merged
merged 1 commit into from
Jan 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 43 additions & 39 deletions src/librustc_mir/borrow_check/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
);

self.add_closure_invoked_twice_with_moved_variable_suggestion(
self.add_moved_or_invoked_closure_note(
context.loc,
used_place,
&mut err,
Expand Down Expand Up @@ -1331,7 +1331,8 @@ enum StorageDeadOrDrop<'tcx> {

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

/// Adds a suggestion when a closure is invoked twice with a moved variable.
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
/// is moved after being invoked.
///
/// ```text
/// note: closure cannot be invoked more than once because it moves the variable `dict` out of
Expand All @@ -1341,30 +1342,18 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
/// LL | for (key, value) in dict {
/// | ^^^^
/// ```
pub(super) fn add_closure_invoked_twice_with_moved_variable_suggestion(
pub(super) fn add_moved_or_invoked_closure_note(
&self,
location: Location,
place: &Place<'tcx>,
diag: &mut DiagnosticBuilder<'_>,
) {
debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
let mut target = place.local();
debug!(
"add_closure_invoked_twice_with_moved_variable_suggestion: location={:?} place={:?} \
target={:?}",
location, place, target,
);
for stmt in &self.mir[location.block].statements[location.statement_index..] {
debug!(
"add_closure_invoked_twice_with_moved_variable_suggestion: stmt={:?} \
target={:?}",
stmt, target,
);
debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind {
debug!(
"add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \
from={:?}",
into, from,
);
debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
match from {
Operand::Copy(ref place) |
Operand::Move(ref place) if target == place.local() =>
Expand All @@ -1374,12 +1363,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
}
}


// Check if we are attempting to call a closure after it has been invoked.
let terminator = self.mir[location.block].terminator();
debug!(
"add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}",
terminator,
);
debug!("add_moved_or_invoked_closure_note: terminator={:?}", terminator);
if let TerminatorKind::Call {
func: Operand::Constant(box Constant {
literal: ty::Const { ty: &ty::TyS { sty: ty::TyKind::FnDef(id, _), .. }, .. },
Expand All @@ -1388,41 +1374,59 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
args,
..
} = &terminator.kind {
debug!("add_closure_invoked_twice_with_moved_variable_suggestion: id={:?}", id);
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
let closure = match args.first() {
Some(Operand::Copy(ref place)) |
Some(Operand::Move(ref place)) if target == place.local() =>
place.local().unwrap(),
_ => return,
};
debug!(
"add_closure_invoked_twice_with_moved_variable_suggestion: closure={:?}",
closure,
);

if let ty::TyKind::Closure(did, _substs) = self.mir.local_decls[closure].ty.sty {
let node_id = match self.infcx.tcx.hir().as_local_node_id(did) {
Some(node_id) => node_id,
_ => return,
};
debug!("add_moved_or_invoked_closure_note: closure={:?}", closure);
if let ty::TyKind::Closure(did, _) = self.mir.local_decls[closure].ty.sty {
let node_id = self.infcx.tcx.hir().as_local_node_id(did).unwrap();
let hir_id = self.infcx.tcx.hir().node_to_hir_id(node_id);

if let Some((
span, name
)) = self.infcx.tcx.typeck_tables_of(did).closure_kind_origins().get(hir_id) {
if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
.closure_kind_origins()
.get(hir_id)
{
diag.span_note(
*span,
&format!(
"closure cannot be invoked more than once because it \
moves the variable `{}` out of its environment",
name,
"closure cannot be invoked more than once because it moves the \
variable `{}` out of its environment",
name,
),
);
return;
}
}
}
}

// Check if we are just moving a closure after it has been invoked.
if let Some(target) = target {
if let ty::TyKind::Closure(did, _) = self.mir.local_decls[target].ty.sty {
let node_id = self.infcx.tcx.hir().as_local_node_id(did).unwrap();
let hir_id = self.infcx.tcx.hir().node_to_hir_id(node_id);

if let Some((span, name)) = self.infcx.tcx.typeck_tables_of(did)
.closure_kind_origins()
.get(hir_id)
{
diag.span_note(
*span,
&format!(
"closure cannot be moved more than once as it is not `Copy` due to \
moving the variable `{}` out of its environment",
name
),
);
}
}
}
}

/// End-user visible description of `place` if one can be found. If the
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/not-copy-closure.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ LL | let b = hello;
| ----- value moved here
LL | let c = hello; //~ ERROR use of moved value: `hello` [E0382]
| ^^^^^ value used here after move
|
note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `a` out of its environment
--> $DIR/not-copy-closure.rs:6:9
|
LL | a += 1;
| ^

error: aborting due to previous error

Expand Down