use rustc::hir::def_id::DefId;
use rustc::middle::region::ScopeTree;
use rustc::mir::{
- self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, Field, Local,
+ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, Constant, Field, Local,
LocalDecl, LocalKind, Location, Operand, Place, PlaceProjection, ProjectionElem,
Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
};
-use rustc::ty;
+use rustc::ty::{self, DefIdTree};
use rustc::util::ppaux::with_highlight_region_for_bound_region;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
&mut self,
context: Context,
desired_action: InitializationRequiringAction,
- (place, span): (&Place<'tcx>, Span),
+ (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span),
mpi: MovePathIndex,
) {
debug!(
- "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} place={:?} \
- span={:?} mpi={:?}",
- context, desired_action, place, span, mpi
+ "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} \
+ moved_place={:?} used_place={:?} span={:?} mpi={:?}",
+ context, desired_action, moved_place, used_place, span, mpi
);
- let use_spans = self.move_spans(place, context.loc)
+ let use_spans = self.move_spans(moved_place, context.loc)
.or_else(|| self.borrow_spans(span, context.loc));
let span = use_spans.args_or_use();
.collect();
if move_out_indices.is_empty() {
- let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
+ let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap();
- if self.uninitialized_error_reported
- .contains(&root_place.clone())
- {
+ if self.uninitialized_error_reported.contains(root_place) {
debug!(
"report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
root_place
self.uninitialized_error_reported.insert(root_place.clone());
- let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
+ let item_msg = match self.describe_place_with_options(used_place,
+ IncludingDowncast(true)) {
Some(name) => format!("`{}`", name),
None => "value".to_owned(),
};
let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable(
span,
desired_action.as_noun(),
- &self.describe_place_with_options(place, IncludingDowncast(true))
- .unwrap_or("_".to_owned()),
+ &self.describe_place_with_options(moved_place, IncludingDowncast(true))
+ .unwrap_or_else(|| "_".to_owned()),
Origin::Mir,
);
err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
use_spans.var_span_label(
&mut err,
- format!("{} occurs due to use in closure", desired_action.as_noun()),
+ format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
);
err.buffer(&mut self.errors_buffer);
} else {
if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
if self.prefixes(&reported_place, PrefixSet::All)
- .any(|p| p == place)
+ .any(|p| p == used_place)
{
debug!(
"report_use_of_moved_or_uninitialized place: error suppressed \
span,
desired_action.as_noun(),
msg,
- self.describe_place_with_options(&place, IncludingDowncast(true)),
+ self.describe_place_with_options(&moved_place, IncludingDowncast(true)),
Origin::Mir,
);
+ self.add_closure_invoked_twice_with_moved_variable_suggestion(
+ context.loc,
+ used_place,
+ &mut err,
+ );
+
let mut is_loop_move = false;
for move_site in &move_site_vec {
let move_out = self.move_data.moves[(*move_site).moi];
);
} else {
err.span_label(move_span, format!("value moved{} here", move_msg));
- move_spans.var_span_label(&mut err, "variable moved due to use in closure");
+ move_spans.var_span_label(
+ &mut err,
+ format!("variable moved due to use{}", move_spans.describe()),
+ );
};
}
use_spans.var_span_label(
&mut err,
- format!("{} occurs due to use in closure", desired_action.as_noun()),
+ format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
);
if !is_loop_move {
);
}
- if let Some(ty) = self.retrieve_type_for_place(place) {
+ if let Some(ty) = self.retrieve_type_for_place(used_place) {
let needs_note = match ty.sty {
ty::Closure(id, _) => {
let tables = self.infcx.tcx.typeck_tables_of(id);
let node_id = self.infcx.tcx.hir.as_local_node_id(id).unwrap();
let hir_id = self.infcx.tcx.hir.node_to_hir_id(node_id);
- if tables.closure_kind_origins().get(hir_id).is_some() {
- false
- } else {
- true
- }
+
+ tables.closure_kind_origins().get(hir_id).is_none()
}
_ => true,
};
}
if let Some((_, mut old_err)) = self.move_error_reported
- .insert(move_out_indices, (place.clone(), err))
+ .insert(move_out_indices, (used_place.clone(), err))
{
// Cancel the old error so it doesn't ICE.
old_err.cancel();
pub(super) fn report_move_out_while_borrowed(
&mut self,
context: Context,
- (place, _span): (&Place<'tcx>, Span),
+ (place, span): (&Place<'tcx>, Span),
borrow: &BorrowData<'tcx>,
) {
+ debug!(
+ "report_move_out_while_borrowed: context={:?} place={:?} span={:?} borrow={:?}",
+ context, place, span, borrow
+ );
let tcx = self.infcx.tcx;
let value_msg = match self.describe_place(place) {
Some(name) => format!("`{}`", name),
let mut err = tcx.cannot_move_when_borrowed(
span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
Origin::Mir,
);
err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
err.span_label(span, format!("move out of {} occurs here", value_msg));
- borrow_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
+ borrow_spans.var_span_label(
+ &mut err,
+ format!("borrow occurs due to use{}", borrow_spans.describe())
+ );
- move_spans.var_span_label(&mut err, "move occurs due to use in closure");
+ move_spans.var_span_label(
+ &mut err,
+ format!("move occurs due to use{}", move_spans.describe())
+ );
self.explain_why_borrow_contains_point(context, borrow, None)
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
let mut err = tcx.cannot_use_when_mutably_borrowed(
span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
borrow_span,
&self.describe_place(&borrow.borrowed_place)
- .unwrap_or("_".to_owned()),
+ .unwrap_or_else(|| "_".to_owned()),
Origin::Mir,
);
borrow_spans.var_span_label(&mut err, {
let place = &borrow.borrowed_place;
- let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
+ let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned());
- format!("borrow occurs due to use of `{}` in closure", desc_place)
+ format!("borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe())
});
self.explain_why_borrow_contains_point(context, borrow, None)
let borrow_spans = self.borrow_spans(span, context.loc);
let span = borrow_spans.args_or_use();
- let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
+ let container_name = if issued_spans.for_generator() || borrow_spans.for_generator() {
+ "generator"
+ } else {
+ "closure"
+ };
+
+ let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned());
let tcx = self.infcx.tcx;
let first_borrow_desc;
);
borrow_spans.var_span_label(
&mut err,
- format!("borrow occurs due to use of `{}` in closure", desc_place),
+ format!(
+ "borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()
+ ),
);
err.buffer(&mut self.errors_buffer);
first_borrow_desc = "first ";
tcx.cannot_uniquely_borrow_by_one_closure(
span,
+ container_name,
&desc_place,
"",
issued_span,
first_borrow_desc = "first ";
tcx.cannot_reborrow_already_uniquely_borrowed(
span,
+ container_name,
&desc_place,
"",
lft,
first_borrow_desc = "first ";
tcx.cannot_reborrow_already_uniquely_borrowed(
span,
+ container_name,
&desc_place,
"",
lft,
if issued_spans == borrow_spans {
borrow_spans.var_span_label(
&mut err,
- format!("borrows occur due to use of `{}` in closure", desc_place),
+ format!("borrows occur due to use of `{}`{}", desc_place, borrow_spans.describe()),
);
} else {
let borrow_place = &issued_borrow.borrowed_place;
- let borrow_place_desc = self.describe_place(borrow_place).unwrap_or("_".to_owned());
+ let borrow_place_desc = self.describe_place(borrow_place)
+ .unwrap_or_else(|| "_".to_owned());
issued_spans.var_span_label(
&mut err,
format!(
- "first borrow occurs due to use of `{}` in closure",
- borrow_place_desc
+ "first borrow occurs due to use of `{}`{}",
+ borrow_place_desc,
+ issued_spans.describe(),
),
);
borrow_spans.var_span_label(
&mut err,
format!(
- "second borrow occurs due to use of `{}` in closure",
- desc_place
+ "second borrow occurs due to use of `{}`{}",
+ desc_place,
+ borrow_spans.describe(),
),
);
}
fn report_local_value_does_not_live_long_enough(
&mut self,
context: Context,
- name: &String,
+ name: &str,
scope_tree: &Lrc<ScopeTree>,
borrow: &BorrowData<'tcx>,
drop_span: Span,
format!("`{}` dropped here while still borrowed", name),
);
- borrow_spans.args_span_label(&mut err, "value captured here");
+ let within = if borrow_spans.for_generator() {
+ " by generator"
+ } else {
+ ""
+ };
+
+ borrow_spans.args_span_label(
+ &mut err,
+ format!("value captured here{}", within),
+ );
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
}
}
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
- borrow_spans.args_span_label(&mut err, "value captured here");
+ let within = if borrow_spans.for_generator() {
+ " by generator"
+ } else {
+ ""
+ };
+
+ borrow_spans.args_span_label(
+ &mut err,
+ format!("value captured here{}", within),
+ );
err
}
(predecessor, is_back_edge)
}));
- let mut visited = FxHashSet();
+ let mut visited = FxHashSet::default();
let mut result = vec![];
'dfs: while let Some((location, is_back_edge)) = stack.pop() {
tcx.cannot_mutate_in_match_guard(
span,
loan_span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
"assign",
Origin::Mir,
)
tcx.cannot_assign_to_borrowed(
span,
loan_span,
- &self.describe_place(place).unwrap_or("_".to_owned()),
+ &self.describe_place(place).unwrap_or_else(|| "_".to_owned()),
Origin::Mir,
)
};
- loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
+ loan_spans.var_span_label(
+ &mut err,
+ format!("borrow occurs due to use{}", loan_spans.describe()),
+ );
self.explain_why_borrow_contains_point(context, loan, None)
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
}
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
- // End-user visible description of `place` if one can be found. If the
- // place is a temporary for instance, None will be returned.
+
+ /// Adds a suggestion when a closure is invoked twice with a moved variable.
+ ///
+ /// ```text
+ /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
+ /// its environment
+ /// --> $DIR/issue-42065.rs:16:29
+ /// |
+ /// LL | for (key, value) in dict {
+ /// | ^^^^
+ /// ```
+ pub(super) fn add_closure_invoked_twice_with_moved_variable_suggestion(
+ &self,
+ location: Location,
+ place: &Place<'tcx>,
+ diag: &mut DiagnosticBuilder<'_>,
+ ) {
+ 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,
+ );
+ if let StatementKind::Assign(into, box Rvalue::Use(from)) = &stmt.kind {
+ debug!(
+ "add_closure_invoked_twice_with_moved_variable_suggestion: into={:?} \
+ from={:?}",
+ into, from,
+ );
+ match from {
+ Operand::Copy(ref place) |
+ Operand::Move(ref place) if target == place.local() =>
+ target = into.local(),
+ _ => {},
+ }
+ }
+ }
+
+
+ let terminator = self.mir[location.block].terminator();
+ debug!(
+ "add_closure_invoked_twice_with_moved_variable_suggestion: terminator={:?}",
+ terminator,
+ );
+ if let TerminatorKind::Call {
+ func: Operand::Constant(box Constant {
+ literal: ty::Const { ty: &ty::TyS { sty: ty::TyKind::FnDef(id, _), .. }, .. },
+ ..
+ }),
+ args,
+ ..
+ } = &terminator.kind {
+ debug!("add_closure_invoked_twice_with_moved_variable_suggestion: 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,
+ };
+ 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 invoked more than once because it \
+ moves the variable `{}` out of its environment",
+ name,
+ ),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /// End-user visible description of `place` if one can be found. If the
+ /// place is a temporary for instance, None will be returned.
pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
self.describe_place_with_options(place, IncludingDowncast(false))
}
- // End-user visible description of `place` if one can be found. If the
- // place is a temporary for instance, None will be returned.
- // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
- // `Downcast` and `IncludingDowncast` is true
+ /// End-user visible description of `place` if one can be found. If the
+ /// place is a temporary for instance, None will be returned.
+ /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
+ /// `Downcast` and `IncludingDowncast` is true
pub(super) fn describe_place_with_options(
&self,
place: &Place<'tcx>,
}
}
- // Appends end-user visible description of `place` to `buf`.
+ /// Appends end-user visible description of `place` to `buf`.
fn append_place_to_string(
&self,
place: &Place<'tcx>,
Ok(())
}
- // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
- // a name, then `Err` is returned
+ /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
+ /// a name, then `Err` is returned
fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
let local = &self.mir.local_decls[local_index];
match local.name {
}
}
- // End-user visible description of the `field`nth field of `base`
+ /// End-user visible description of the `field`nth field of `base`
fn describe_field(&self, base: &Place, field: Field) -> String {
match *base {
Place::Local(local) => {
Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
Place::Projection(ref proj) => match proj.elem {
ProjectionElem::Deref => self.describe_field(&proj.base, field),
- ProjectionElem::Downcast(def, variant_index) => format!(
- "{}",
- def.variants[variant_index].fields[field.index()].ident
- ),
+ ProjectionElem::Downcast(def, variant_index) =>
+ def.variants[variant_index].fields[field.index()].ident.to_string(),
ProjectionElem::Field(_, field_type) => {
self.describe_field_from_ty(&field_type, field)
}
}
}
- // End-user visible description of the `field_index`nth field of `ty`
+ /// End-user visible description of the `field_index`nth field of `ty`
fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
if ty.is_box() {
// If the type is a box, the field is described from the boxed type
}
}
- // Retrieve type of a place for the current MIR representation
+ /// Retrieve type of a place for the current MIR representation
fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
match place {
Place::Local(local) => {
"annotate_argument_and_return_for_borrow: location={:?}",
location
);
- match &self.mir[location.block]
- .statements
- .get(location.statement_index)
+ if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..})
+ = &self.mir[location.block].statements.get(location.statement_index)
{
- Some(&Statement {
- kind: StatementKind::Assign(ref reservation, _),
- ..
- }) => {
+ debug!(
+ "annotate_argument_and_return_for_borrow: reservation={:?}",
+ reservation
+ );
+ // Check that the initial assignment of the reserve location is into a temporary.
+ let mut target = *match reservation {
+ Place::Local(local) if self.mir.local_kind(*local) == LocalKind::Temp => local,
+ _ => return None,
+ };
+
+ // Next, look through the rest of the block, checking if we are assigning the
+ // `target` (that is, the place that contains our borrow) to anything.
+ let mut annotated_closure = None;
+ for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
debug!(
- "annotate_argument_and_return_for_borrow: reservation={:?}",
- reservation
+ "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
+ target, stmt
);
- // Check that the initial assignment of the reserve location is into a temporary.
- let mut target = *match reservation {
- Place::Local(local) if self.mir.local_kind(*local) == LocalKind::Temp => local,
- _ => return None,
- };
-
- // Next, look through the rest of the block, checking if we are assigning the
- // `target` (that is, the place that contains our borrow) to anything.
- let mut annotated_closure = None;
- for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+ if let StatementKind::Assign(Place::Local(assigned_to), box rvalue) = &stmt.kind
+ {
debug!(
- "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
- target, stmt
+ "annotate_argument_and_return_for_borrow: assigned_to={:?} \
+ rvalue={:?}",
+ assigned_to, rvalue
);
- if let StatementKind::Assign(Place::Local(assigned_to), box rvalue) = &stmt.kind
+ // Check if our `target` was captured by a closure.
+ if let Rvalue::Aggregate(
+ box AggregateKind::Closure(def_id, substs),
+ operands,
+ ) = rvalue
{
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_to={:?} \
- rvalue={:?}",
- assigned_to, rvalue
- );
- // Check if our `target` was captured by a closure.
- if let Rvalue::Aggregate(
- box AggregateKind::Closure(def_id, substs),
- operands,
- ) = rvalue
- {
- for operand in operands {
- let assigned_from = match operand {
- Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
- assigned_from
- }
- _ => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+ for operand in operands {
+ let assigned_from = match operand {
+ Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
assigned_from
- );
-
- // Find the local from the operand.
- let assigned_from_local = match assigned_from.local() {
- Some(local) => local,
- None => continue,
- };
-
- if assigned_from_local != target {
- continue;
}
+ _ => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+ assigned_from
+ );
- // If a closure captured our `target` and then assigned
- // into a place then we should annotate the closure in
- // case it ends up being assigned into the return place.
- annotated_closure = self.annotate_fn_sig(
- *def_id,
- self.infcx.closure_sig(*def_id, *substs),
- );
- debug!(
- "annotate_argument_and_return_for_borrow: \
- annotated_closure={:?} assigned_from_local={:?} \
- assigned_to={:?}",
- annotated_closure, assigned_from_local, assigned_to
- );
-
- if *assigned_to == mir::RETURN_PLACE {
- // If it was assigned directly into the return place, then
- // return now.
- return annotated_closure;
- } else {
- // Otherwise, update the target.
- target = *assigned_to;
- }
+ // Find the local from the operand.
+ let assigned_from_local = match assigned_from.local() {
+ Some(local) => local,
+ None => continue,
+ };
+
+ if assigned_from_local != target {
+ continue;
}
- // If none of our closure's operands matched, then skip to the next
- // statement.
- continue;
+ // If a closure captured our `target` and then assigned
+ // into a place then we should annotate the closure in
+ // case it ends up being assigned into the return place.
+ annotated_closure = self.annotate_fn_sig(
+ *def_id,
+ self.infcx.closure_sig(*def_id, *substs),
+ );
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ annotated_closure={:?} assigned_from_local={:?} \
+ assigned_to={:?}",
+ annotated_closure, assigned_from_local, assigned_to
+ );
+
+ if *assigned_to == mir::RETURN_PLACE {
+ // If it was assigned directly into the return place, then
+ // return now.
+ return annotated_closure;
+ } else {
+ // Otherwise, update the target.
+ target = *assigned_to;
+ }
}
- // Otherwise, look at other types of assignment.
- let assigned_from = match rvalue {
- Rvalue::Ref(_, _, assigned_from) => assigned_from,
- Rvalue::Use(operand) => match operand {
- Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
- assigned_from
- }
- _ => continue,
- },
- _ => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: \
- assigned_from={:?}",
- assigned_from,
- );
+ // If none of our closure's operands matched, then skip to the next
+ // statement.
+ continue;
+ }
- // Find the local from the rvalue.
- let assigned_from_local = match assigned_from.local() {
- Some(local) => local,
- None => continue,
- };
- debug!(
- "annotate_argument_and_return_for_borrow: \
- assigned_from_local={:?}",
- assigned_from_local,
- );
+ // Otherwise, look at other types of assignment.
+ let assigned_from = match rvalue {
+ Rvalue::Ref(_, _, assigned_from) => assigned_from,
+ Rvalue::Use(operand) => match operand {
+ Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
+ assigned_from
+ }
+ _ => continue,
+ },
+ _ => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ assigned_from={:?}",
+ assigned_from,
+ );
- // Check if our local matches the target - if so, we've assigned our
- // borrow to a new place.
- if assigned_from_local != target {
- continue;
- }
+ // Find the local from the rvalue.
+ let assigned_from_local = match assigned_from.local() {
+ Some(local) => local,
+ None => continue,
+ };
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ assigned_from_local={:?}",
+ assigned_from_local,
+ );
- // If we assigned our `target` into a new place, then we should
- // check if it was the return place.
- debug!(
- "annotate_argument_and_return_for_borrow: \
- assigned_from_local={:?} assigned_to={:?}",
- assigned_from_local, assigned_to
- );
- if *assigned_to == mir::RETURN_PLACE {
- // If it was then return the annotated closure if there was one,
- // else, annotate this function.
- return annotated_closure.or_else(fallback);
- }
+ // Check if our local matches the target - if so, we've assigned our
+ // borrow to a new place.
+ if assigned_from_local != target {
+ continue;
+ }
- // If we didn't assign into the return place, then we just update
- // the target.
- target = *assigned_to;
+ // If we assigned our `target` into a new place, then we should
+ // check if it was the return place.
+ debug!(
+ "annotate_argument_and_return_for_borrow: \
+ assigned_from_local={:?} assigned_to={:?}",
+ assigned_from_local, assigned_to
+ );
+ if *assigned_to == mir::RETURN_PLACE {
+ // If it was then return the annotated closure if there was one,
+ // else, annotate this function.
+ return annotated_closure.or_else(fallback);
}
+
+ // If we didn't assign into the return place, then we just update
+ // the target.
+ target = *assigned_to;
}
+ }
- // Check the terminator if we didn't find anything in the statements.
- let terminator = &self.mir[location.block].terminator();
+ // Check the terminator if we didn't find anything in the statements.
+ let terminator = &self.mir[location.block].terminator();
+ debug!(
+ "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
+ target, terminator
+ );
+ if let TerminatorKind::Call {
+ destination: Some((Place::Local(assigned_to), _)),
+ args,
+ ..
+ } = &terminator.kind
+ {
debug!(
- "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
- target, terminator
+ "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
+ assigned_to, args
);
- if let TerminatorKind::Call {
- destination: Some((Place::Local(assigned_to), _)),
- args,
- ..
- } = &terminator.kind
- {
+ for operand in args {
+ let assigned_from = match operand {
+ Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
+ assigned_from
+ }
+ _ => continue,
+ };
debug!(
- "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
- assigned_to, args
+ "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+ assigned_from,
);
- for operand in args {
- let assigned_from = match operand {
- Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
- assigned_from
- }
- _ => continue,
- };
+
+ if let Some(assigned_from_local) = assigned_from.local() {
debug!(
- "annotate_argument_and_return_for_borrow: assigned_from={:?}",
- assigned_from,
+ "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
+ assigned_from_local,
);
- if let Some(assigned_from_local) = assigned_from.local() {
- debug!(
- "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
- assigned_from_local,
- );
-
- if *assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
- return annotated_closure.or_else(fallback);
- }
+ if *assigned_to == mir::RETURN_PLACE && assigned_from_local == target {
+ return annotated_closure.or_else(fallback);
}
}
}
}
- _ => {}
}
// If we haven't found an assignment into the return place, then we need not add
// Need to use the `rustc::ty` types to compare against the
// `return_region`. Then use the `rustc::hir` type to get only
// the lifetime span.
- match &fn_decl.inputs[index].node {
- hir::TyKind::Rptr(lifetime, _) => {
- // With access to the lifetime, we can get
- // the span of it.
- arguments.push((*argument, lifetime.span));
- }
- _ => bug!("ty type is a ref but hir type is not"),
+ if let hir::TyKind::Rptr(lifetime, _) = &fn_decl.inputs[index].node {
+ // With access to the lifetime, we can get
+ // the span of it.
+ arguments.push((*argument, lifetime.span));
+ } else {
+ bug!("ty type is a ref but hir type is not");
}
}
}
ty::RegionKind::RePlaceholder(ty::Placeholder { name: br, .. }),
_,
_,
- ) => with_highlight_region_for_bound_region(*br, counter, || format!("{}", ty)),
- _ => format!("{}", ty),
+ ) => with_highlight_region_for_bound_region(*br, counter, || ty.to_string()),
+ _ => ty.to_string(),
}
}
ty::TyKind::Ref(region, _, _) => match region {
ty::RegionKind::ReLateBound(_, br)
| ty::RegionKind::RePlaceholder(ty::Placeholder { name: br, .. }) => {
- with_highlight_region_for_bound_region(*br, counter, || format!("{}", region))
+ with_highlight_region_for_bound_region(*br, counter, || region.to_string())
}
- _ => format!("{}", region),
+ _ => region.to_string(),
},
_ => bug!("ty for annotation of borrow region is not a reference"),
}
pub(super) enum UseSpans {
// The access is caused by capturing a variable for a closure.
ClosureUse {
+ // This is true if the captured variable was from a generator.
+ is_generator: bool,
// The span of the args of the closure, including the `move` keyword if
// it's present.
args_span: Span,
}
}
- pub(super) fn for_closure(self) -> bool {
- match self {
- UseSpans::ClosureUse { .. } => true,
- UseSpans::OtherUse(_) => false,
+ /// Return `false` if this place is not used in a closure.
+ fn for_closure(&self) -> bool {
+ match *self {
+ UseSpans::ClosureUse { is_generator, .. } => !is_generator,
+ _ => false,
+ }
+ }
+
+ /// Return `false` if this place is not used in a generator.
+ fn for_generator(&self) -> bool {
+ match *self {
+ UseSpans::ClosureUse { is_generator, .. } => is_generator,
+ _ => false,
+ }
+ }
+
+ /// Describe the span associated with a use of a place.
+ fn describe(&self) -> String {
+ match *self {
+ UseSpans::ClosureUse { is_generator, .. } => if is_generator {
+ " in generator".to_string()
+ } else {
+ " in closure".to_string()
+ },
+ _ => "".to_string(),
}
}
location: Location,
) -> UseSpans {
use self::UseSpans::*;
- use rustc::hir::ExprKind::Closure;
- use rustc::mir::AggregateKind;
- let stmt = match self.mir[location.block]
- .statements
- .get(location.statement_index)
- {
+ let stmt = match self.mir[location.block].statements.get(location.statement_index) {
Some(stmt) => stmt,
None => return OtherUse(self.mir.source_info(location).span),
};
- if let StatementKind::Assign(_, box Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
- if let AggregateKind::Closure(def_id, _) = **kind {
- debug!("find_closure_move_span: found closure {:?}", places);
+ debug!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place, location, stmt);
+ if let StatementKind::Assign(
+ _,
+ box Rvalue::Aggregate(ref kind, ref places)
+ ) = stmt.kind {
+ let (def_id, is_generator) = match kind {
+ box AggregateKind::Closure(def_id, _) => (def_id, false),
+ box AggregateKind::Generator(def_id, _, _) => (def_id, true),
+ _ => return OtherUse(stmt.source_info.span),
+ };
- if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) {
- if let Closure(_, _, _, args_span, _) =
- self.infcx.tcx.hir.expect_expr(node_id).node
- {
- if let Some(var_span) = self.infcx.tcx.with_freevars(node_id, |freevars| {
- for (v, place) in freevars.iter().zip(places) {
- match place {
- Operand::Copy(place) | Operand::Move(place)
- if moved_place == place =>
- {
- debug!(
- "find_closure_move_span: found captured local {:?}",
- place
- );
- return Some(v.span);
- }
- _ => {}
- }
- }
- None
- }) {
- return ClosureUse {
- args_span,
- var_span,
- };
- }
- }
- }
+ debug!(
+ "move_spans: def_id={:?} is_generator={:?} places={:?}",
+ def_id, is_generator, places
+ );
+ if let Some((args_span, var_span)) = self.closure_span(*def_id, moved_place, places) {
+ return ClosureUse {
+ is_generator,
+ args_span,
+ var_span,
+ };
}
}
- return OtherUse(stmt.source_info.span);
+ OtherUse(stmt.source_info.span)
}
/// Finds the span of arguments of a closure (within `maybe_closure_span`)
/// and originating from `maybe_closure_span`.
pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
use self::UseSpans::*;
- use rustc::hir::ExprKind::Closure;
+ debug!("borrow_spans: use_span={:?} location={:?}", use_span, location);
- let local = match self.mir[location.block]
+ let target = match self.mir[location.block]
.statements
.get(location.statement_index)
{
_ => return OtherUse(use_span),
};
- if self.mir.local_kind(local) != LocalKind::Temp {
+ if self.mir.local_kind(target) != LocalKind::Temp {
// operands are always temporaries.
return OtherUse(use_span);
}
for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
- if let StatementKind::Assign(_, box Rvalue::Aggregate(ref kind, ref places)) = stmt.kind
- {
- if let AggregateKind::Closure(def_id, _) = **kind {
- debug!("find_closure_borrow_span: found closure {:?}", places);
-
- return if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) {
- let args_span = if let Closure(_, _, _, span, _) =
- self.infcx.tcx.hir.expect_expr(node_id).node
- {
- span
- } else {
- return OtherUse(use_span);
- };
+ if let StatementKind::Assign(
+ _, box Rvalue::Aggregate(ref kind, ref places)
+ ) = stmt.kind {
+ let (def_id, is_generator) = match kind {
+ box AggregateKind::Closure(def_id, _) => (def_id, false),
+ box AggregateKind::Generator(def_id, _, _) => (def_id, true),
+ _ => continue,
+ };
- self.infcx
- .tcx
- .with_freevars(node_id, |freevars| {
- for (v, place) in freevars.iter().zip(places) {
- match *place {
- Operand::Copy(Place::Local(l))
- | Operand::Move(Place::Local(l)) if local == l =>
- {
- debug!(
- "find_closure_borrow_span: found captured local \
- {:?}",
- l
- );
- return Some(v.span);
- }
- _ => {}
- }
- }
- None
- })
- .map(|var_span| ClosureUse {
- args_span,
- var_span,
- })
- .unwrap_or(OtherUse(use_span))
- } else {
- OtherUse(use_span)
+ debug!(
+ "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
+ def_id, is_generator, places
+ );
+ if let Some((args_span, var_span)) = self.closure_span(
+ *def_id, &Place::Local(target), places
+ ) {
+ return ClosureUse {
+ is_generator,
+ args_span,
+ var_span,
};
+ } else {
+ return OtherUse(use_span);
}
}
OtherUse(use_span)
}
+ /// Finds the span of a captured variable within a closure or generator.
+ fn closure_span(
+ &self,
+ def_id: DefId,
+ target_place: &Place<'tcx>,
+ places: &Vec<Operand<'tcx>>,
+ ) -> Option<(Span, Span)> {
+ debug!(
+ "closure_span: def_id={:?} target_place={:?} places={:?}",
+ def_id, target_place, places
+ );
+ let node_id = self.infcx.tcx.hir.as_local_node_id(def_id)?;
+ let expr = &self.infcx.tcx.hir.expect_expr(node_id).node;
+ debug!("closure_span: node_id={:?} expr={:?}", node_id, expr);
+ if let hir::ExprKind::Closure(
+ .., args_span, _
+ ) = expr {
+ let var_span = self.infcx.tcx.with_freevars(
+ node_id,
+ |freevars| {
+ for (v, place) in freevars.iter().zip(places) {
+ match place {
+ Operand::Copy(place) |
+ Operand::Move(place) if target_place == place => {
+ debug!("closure_span: found captured local {:?}", place);
+ return Some(v.span);
+ },
+ _ => {}
+ }
+ }
+
+ None
+ },
+ )?;
+
+ Some((*args_span, var_span))
+ } else {
+ None
+ }
+ }
+
/// Helper to retrieve span(s) of given borrow from the current MIR
/// representation
pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData) -> UseSpans {