1 use crate::borrow_check
::nll
::explain_borrow
::BorrowExplanation
;
2 use crate::borrow_check
::nll
::region_infer
::{RegionName, RegionNameSource}
;
3 use crate::borrow_check
::prefixes
::IsPrefixOf
;
4 use crate::borrow_check
::WriteKind
;
6 use rustc
::hir
::def_id
::DefId
;
7 use rustc
::middle
::region
::ScopeTree
;
9 self, AggregateKind
, BindingForm
, BorrowKind
, ClearCrossCrate
, Constant
,
10 ConstraintCategory
, Field
, Local
, LocalDecl
, LocalKind
, Location
, Operand
,
11 Place
, PlaceProjection
, ProjectionElem
, Rvalue
, Statement
, StatementKind
,
12 TerminatorKind
, VarBindingForm
,
14 use rustc
::ty
::{self, DefIdTree}
;
15 use rustc
::util
::ppaux
::RegionHighlightMode
;
16 use rustc_data_structures
::fx
::FxHashSet
;
17 use rustc_data_structures
::indexed_vec
::Idx
;
18 use rustc_data_structures
::sync
::Lrc
;
19 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
22 use super::borrow_set
::BorrowData
;
23 use super::{Context, MirBorrowckCtxt}
;
24 use super::{InitializationRequiringAction, PrefixSet}
;
25 use crate::dataflow
::drop_flag_effects
;
26 use crate::dataflow
::move_paths
::indexes
::MoveOutIndex
;
27 use crate::dataflow
::move_paths
::MovePathIndex
;
28 use crate::util
::borrowck_errors
::{BorrowckErrors, Origin}
;
32 /// Index of the "move out" that we found. The `MoveData` can
33 /// then tell us where the move occurred.
36 /// `true` if we traversed a back edge while walking from the point
37 /// of error to the move site.
38 traversed_back_edge
: bool
41 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
42 pub(super) fn report_use_of_moved_or_uninitialized(
45 desired_action
: InitializationRequiringAction
,
46 (moved_place
, used_place
, span
): (&Place
<'tcx
>, &Place
<'tcx
>, Span
),
50 "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} \
51 moved_place={:?} used_place={:?} span={:?} mpi={:?}",
52 context
, desired_action
, moved_place
, used_place
, span
, mpi
55 let use_spans
= self.move_spans(moved_place
, context
.loc
)
56 .or_else(|| self.borrow_spans(span
, context
.loc
));
57 let span
= use_spans
.args_or_use();
59 let move_site_vec
= self.get_moved_indexes(context
, mpi
);
61 "report_use_of_moved_or_uninitialized: move_site_vec={:?}",
64 let move_out_indices
: Vec
<_
> = move_site_vec
66 .map(|move_site
| move_site
.moi
)
69 if move_out_indices
.is_empty() {
70 let root_place
= self.prefixes(&used_place
, PrefixSet
::All
).last().unwrap();
72 if self.uninitialized_error_reported
.contains(root_place
) {
74 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
80 self.uninitialized_error_reported
.insert(root_place
.clone());
82 let item_msg
= match self.describe_place_with_options(used_place
,
83 IncludingDowncast(true)) {
84 Some(name
) => format
!("`{}`", name
),
85 None
=> "value".to_owned(),
87 let mut err
= self.infcx
.tcx
.cannot_act_on_uninitialized_variable(
89 desired_action
.as_noun(),
90 &self.describe_place_with_options(moved_place
, IncludingDowncast(true))
91 .unwrap_or_else(|| "_".to_owned()),
94 err
.span_label(span
, format
!("use of possibly uninitialized {}", item_msg
));
96 use_spans
.var_span_label(
98 format
!("{} occurs due to use{}", desired_action
.as_noun(), use_spans
.describe()),
101 err
.buffer(&mut self.errors_buffer
);
103 if let Some((reported_place
, _
)) = self.move_error_reported
.get(&move_out_indices
) {
104 if self.prefixes(&reported_place
, PrefixSet
::All
)
105 .any(|p
| p
== used_place
)
108 "report_use_of_moved_or_uninitialized place: error suppressed \
116 let msg
= ""; //FIXME: add "partially " or "collaterally "
118 let mut err
= self.infcx
.tcx
.cannot_act_on_moved_value(
120 desired_action
.as_noun(),
122 self.describe_place_with_options(&moved_place
, IncludingDowncast(true)),
126 self.add_moved_or_invoked_closure_note(
132 let mut is_loop_move
= false;
133 let is_partial_move
= move_site_vec
.iter().any(|move_site
| {
134 let move_out
= self.move_data
.moves
[(*move_site
).moi
];
135 let moved_place
= &self.move_data
.move_paths
[move_out
.path
].place
;
136 used_place
!= moved_place
&& used_place
.is_prefix_of(moved_place
)
138 for move_site
in &move_site_vec
{
139 let move_out
= self.move_data
.moves
[(*move_site
).moi
];
140 let moved_place
= &self.move_data
.move_paths
[move_out
.path
].place
;
142 let move_spans
= self.move_spans(moved_place
, move_out
.source
);
143 let move_span
= move_spans
.args_or_use();
145 let move_msg
= if move_spans
.for_closure() {
151 if span
== move_span
{
154 format
!("value moved{} here, in previous iteration of loop", move_msg
),
157 } else if move_site
.traversed_back_edge
{
161 "value moved{} here, in previous iteration of loop",
166 err
.span_label(move_span
, format
!("value moved{} here", move_msg
));
167 move_spans
.var_span_label(
169 format
!("variable moved due to use{}", move_spans
.describe()),
174 use_spans
.var_span_label(
176 format
!("{} occurs due to use{}", desired_action
.as_noun(), use_spans
.describe()),
184 desired_action
.as_verb_in_past_tense(),
185 if is_partial_move { "after partial move" }
else { "after move" }
,
190 let ty
= used_place
.ty(self.mir
, self.infcx
.tcx
).to_ty(self.infcx
.tcx
);
191 let needs_note
= match ty
.sty
{
192 ty
::Closure(id
, _
) => {
193 let tables
= self.infcx
.tcx
.typeck_tables_of(id
);
194 let node_id
= self.infcx
.tcx
.hir().as_local_node_id(id
).unwrap();
195 let hir_id
= self.infcx
.tcx
.hir().node_to_hir_id(node_id
);
197 tables
.closure_kind_origins().get(hir_id
).is_none()
203 let mpi
= self.move_data
.moves
[move_out_indices
[0]].path
;
204 let place
= &self.move_data
.move_paths
[mpi
].place
;
206 let ty
= place
.ty(self.mir
, self.infcx
.tcx
).to_ty(self.infcx
.tcx
);
207 let opt_name
= self.describe_place_with_options(place
, IncludingDowncast(true));
208 let note_msg
= match opt_name
{
209 Some(ref name
) => format
!("`{}`", name
),
210 None
=> "value".to_owned(),
212 if let ty
::TyKind
::Param(param_ty
) = ty
.sty
{
213 let tcx
= self.infcx
.tcx
;
214 let generics
= tcx
.generics_of(self.mir_def_id
);
215 let def_id
= generics
.type_param(¶m_ty
, tcx
).def_id
;
216 if let Some(sp
) = tcx
.hir().span_if_local(def_id
) {
219 "consider adding a `Copy` constraint to this type argument",
223 if let Place
::Local(local
) = place
{
224 let decl
= &self.mir
.local_decls
[*local
];
226 decl
.source_info
.span
,
228 "move occurs because {} has type `{}`, \
229 which does not implement the `Copy` trait",
234 "move occurs because {} has type `{}`, \
235 which does not implement the `Copy` trait",
241 if let Some((_
, mut old_err
)) = self.move_error_reported
242 .insert(move_out_indices
, (used_place
.clone(), err
))
244 // Cancel the old error so it doesn't ICE.
250 pub(super) fn report_move_out_while_borrowed(
253 (place
, span
): (&Place
<'tcx
>, Span
),
254 borrow
: &BorrowData
<'tcx
>,
257 "report_move_out_while_borrowed: context={:?} place={:?} span={:?} borrow={:?}",
258 context
, place
, span
, borrow
260 let tcx
= self.infcx
.tcx
;
261 let value_msg
= match self.describe_place(place
) {
262 Some(name
) => format
!("`{}`", name
),
263 None
=> "value".to_owned(),
265 let borrow_msg
= match self.describe_place(&borrow
.borrowed_place
) {
266 Some(name
) => format
!("`{}`", name
),
267 None
=> "value".to_owned(),
270 let borrow_spans
= self.retrieve_borrow_spans(borrow
);
271 let borrow_span
= borrow_spans
.args_or_use();
273 let move_spans
= self.move_spans(place
, context
.loc
);
274 let span
= move_spans
.args_or_use();
276 let mut err
= tcx
.cannot_move_when_borrowed(
278 &self.describe_place(place
).unwrap_or_else(|| "_".to_owned()),
281 err
.span_label(borrow_span
, format
!("borrow of {} occurs here", borrow_msg
));
282 err
.span_label(span
, format
!("move out of {} occurs here", value_msg
));
284 borrow_spans
.var_span_label(
286 format
!("borrow occurs due to use{}", borrow_spans
.describe())
289 move_spans
.var_span_label(
291 format
!("move occurs due to use{}", move_spans
.describe())
294 self.explain_why_borrow_contains_point(context
, borrow
, None
)
295 .add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
296 err
.buffer(&mut self.errors_buffer
);
299 pub(super) fn report_use_while_mutably_borrowed(
302 (place
, _span
): (&Place
<'tcx
>, Span
),
303 borrow
: &BorrowData
<'tcx
>,
305 let tcx
= self.infcx
.tcx
;
307 let borrow_spans
= self.retrieve_borrow_spans(borrow
);
308 let borrow_span
= borrow_spans
.args_or_use();
310 // Conflicting borrows are reported separately, so only check for move
312 let use_spans
= self.move_spans(place
, context
.loc
);
313 let span
= use_spans
.var_or_use();
315 let mut err
= tcx
.cannot_use_when_mutably_borrowed(
317 &self.describe_place(place
).unwrap_or_else(|| "_".to_owned()),
319 &self.describe_place(&borrow
.borrowed_place
)
320 .unwrap_or_else(|| "_".to_owned()),
324 borrow_spans
.var_span_label(&mut err
, {
325 let place
= &borrow
.borrowed_place
;
326 let desc_place
= self.describe_place(place
).unwrap_or_else(|| "_".to_owned());
328 format
!("borrow occurs due to use of `{}`{}", desc_place
, borrow_spans
.describe())
331 self.explain_why_borrow_contains_point(context
, borrow
, None
)
332 .add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
333 err
.buffer(&mut self.errors_buffer
);
336 pub(super) fn report_conflicting_borrow(
339 (place
, span
): (&Place
<'tcx
>, Span
),
340 gen_borrow_kind
: BorrowKind
,
341 issued_borrow
: &BorrowData
<'tcx
>,
343 let issued_spans
= self.retrieve_borrow_spans(issued_borrow
);
344 let issued_span
= issued_spans
.args_or_use();
346 let borrow_spans
= self.borrow_spans(span
, context
.loc
);
347 let span
= borrow_spans
.args_or_use();
349 let container_name
= if issued_spans
.for_generator() || borrow_spans
.for_generator() {
355 let (desc_place
, msg_place
, msg_borrow
, union_type_name
) =
356 self.describe_place_for_conflicting_borrow(place
, &issued_borrow
.borrowed_place
);
358 let explanation
= self.explain_why_borrow_contains_point(context
, issued_borrow
, None
);
359 let second_borrow_desc
= if explanation
.is_explained() {
365 // FIXME: supply non-"" `opt_via` when appropriate
366 let tcx
= self.infcx
.tcx
;
367 let first_borrow_desc
;
368 let mut err
= match (
376 (BorrowKind
::Shared
, lft
, _
, BorrowKind
::Mut { .. }
, _
, rgt
) => {
377 first_borrow_desc
= "mutable ";
378 tcx
.cannot_reborrow_already_borrowed(
391 (BorrowKind
::Mut { .. }
, _
, lft
, BorrowKind
::Shared
, rgt
, _
) => {
392 first_borrow_desc
= "immutable ";
393 tcx
.cannot_reborrow_already_borrowed(
407 (BorrowKind
::Mut { .. }
, _
, _
, BorrowKind
::Mut { .. }
, _
, _
) => {
408 first_borrow_desc
= "first ";
409 tcx
.cannot_mutably_borrow_multiply(
420 (BorrowKind
::Unique
, _
, _
, BorrowKind
::Unique
, _
, _
) => {
421 first_borrow_desc
= "first ";
422 tcx
.cannot_uniquely_borrow_by_two_closures(
431 (BorrowKind
::Mut { .. }
, _
, _
, BorrowKind
::Shallow
, _
, _
)
432 | (BorrowKind
::Unique
, _
, _
, BorrowKind
::Shallow
, _
, _
) => {
433 let mut err
= tcx
.cannot_mutate_in_match_guard(
440 borrow_spans
.var_span_label(
443 "borrow occurs due to use of `{}`{}", desc_place
, borrow_spans
.describe()
446 err
.buffer(&mut self.errors_buffer
);
451 (BorrowKind
::Unique
, _
, _
, _
, _
, _
) => {
452 first_borrow_desc
= "first ";
453 tcx
.cannot_uniquely_borrow_by_one_closure(
466 (BorrowKind
::Shared
, lft
, _
, BorrowKind
::Unique
, _
, _
) => {
467 first_borrow_desc
= "first ";
468 tcx
.cannot_reborrow_already_uniquely_borrowed(
482 (BorrowKind
::Mut { .. }
, _
, lft
, BorrowKind
::Unique
, _
, _
) => {
483 first_borrow_desc
= "first ";
484 tcx
.cannot_reborrow_already_uniquely_borrowed(
498 (BorrowKind
::Shallow
, _
, _
, BorrowKind
::Unique
, _
, _
)
499 | (BorrowKind
::Shallow
, _
, _
, BorrowKind
::Mut { .. }
, _
, _
) => {
500 // Shallow borrows are uses from the user's point of view.
501 self.report_use_while_mutably_borrowed(context
, (place
, span
), issued_borrow
);
504 (BorrowKind
::Shared
, _
, _
, BorrowKind
::Shared
, _
, _
)
505 | (BorrowKind
::Shared
, _
, _
, BorrowKind
::Shallow
, _
, _
)
506 | (BorrowKind
::Shallow
, _
, _
, BorrowKind
::Shared
, _
, _
)
507 | (BorrowKind
::Shallow
, _
, _
, BorrowKind
::Shallow
, _
, _
) => unreachable
!(),
510 if issued_spans
== borrow_spans
{
511 borrow_spans
.var_span_label(
513 format
!("borrows occur due to use of `{}`{}", desc_place
, borrow_spans
.describe()),
516 let borrow_place
= &issued_borrow
.borrowed_place
;
517 let borrow_place_desc
= self.describe_place(borrow_place
)
518 .unwrap_or_else(|| "_".to_owned());
519 issued_spans
.var_span_label(
522 "first borrow occurs due to use of `{}`{}",
524 issued_spans
.describe(),
528 borrow_spans
.var_span_label(
531 "second borrow occurs due to use of `{}`{}",
533 borrow_spans
.describe(),
538 if union_type_name
!= "" {
540 "`{}` is a field of the union `{}`, so it overlaps the field `{}`",
541 msg_place
, union_type_name
, msg_borrow
,
546 .add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, first_borrow_desc
);
548 err
.buffer(&mut self.errors_buffer
);
551 /// Returns the description of the root place for a conflicting borrow and the full
552 /// descriptions of the places that caused the conflict.
554 /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
555 /// attempted while a shared borrow is live, then this function will return:
559 /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
560 /// a shared borrow of another field `x.y`, then this function will return:
562 /// ("x", "x.z", "x.y")
564 /// In the more complex union case, where the union is a field of a struct, then if a mutable
565 /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
566 /// another field `x.u.y`, then this function will return:
568 /// ("x.u", "x.u.z", "x.u.y")
570 /// This is used when creating error messages like below:
572 /// > cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
573 /// > mutable (via `a.u.s.b`) [E0502]
574 pub(super) fn describe_place_for_conflicting_borrow(
576 first_borrowed_place
: &Place
<'tcx
>,
577 second_borrowed_place
: &Place
<'tcx
>,
578 ) -> (String
, String
, String
, String
) {
579 // Define a small closure that we can use to check if the type of a place
581 let is_union
= |place
: &Place
<'tcx
>| -> bool
{
582 place
.ty(self.mir
, self.infcx
.tcx
)
583 .to_ty(self.infcx
.tcx
)
585 .map(|adt
| adt
.is_union())
589 // Start with an empty tuple, so we can use the functions on `Option` to reduce some
590 // code duplication (particularly around returning an empty description in the failure
594 // If we have a conflicting borrow of the same place, then we don't want to add
595 // an extraneous "via x.y" to our diagnostics, so filter out this case.
596 first_borrowed_place
!= second_borrowed_place
599 // We're going to want to traverse the first borrowed place to see if we can find
600 // field access to a union. If we find that, then we will keep the place of the
601 // union being accessed and the field that was being accessed so we can check the
602 // second borrowed place for the same union and a access to a different field.
603 let mut current
= first_borrowed_place
;
604 while let Place
::Projection(box PlaceProjection { base, elem }
) = current
{
606 ProjectionElem
::Field(field
, _
) if is_union(base
) => {
607 return Some((base
, field
));
614 .and_then(|(target_base
, target_field
)| {
615 // With the place of a union and a field access into it, we traverse the second
616 // borrowed place and look for a access to a different field of the same union.
617 let mut current
= second_borrowed_place
;
618 while let Place
::Projection(box PlaceProjection { base, elem }
) = current
{
620 ProjectionElem
::Field(field
, _
) if {
621 is_union(base
) && field
!= target_field
&& base
== target_base
623 let desc_base
= self.describe_place(base
)
624 .unwrap_or_else(|| "_".to_owned());
625 let desc_first
= self.describe_place(first_borrowed_place
)
626 .unwrap_or_else(|| "_".to_owned());
627 let desc_second
= self.describe_place(second_borrowed_place
)
628 .unwrap_or_else(|| "_".to_owned());
630 // Also compute the name of the union type, eg. `Foo` so we
631 // can add a helpful note with it.
632 let ty
= base
.ty(self.mir
, self.infcx
.tcx
).to_ty(self.infcx
.tcx
);
634 return Some((desc_base
, desc_first
, desc_second
, ty
.to_string()));
642 // If we didn't find a field access into a union, or both places match, then
643 // only return the description of the first place.
644 let desc_place
= self.describe_place(first_borrowed_place
)
645 .unwrap_or_else(|| "_".to_owned());
646 (desc_place
, "".to_string(), "".to_string(), "".to_string())
650 /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
652 /// This means that some data referenced by `borrow` needs to live
653 /// past the point where the StorageDeadOrDrop of `place` occurs.
654 /// This is usually interpreted as meaning that `place` has too
655 /// short a lifetime. (But sometimes it is more useful to report
656 /// it as a more direct conflict between the execution of a
657 /// `Drop::drop` with an aliasing borrow.)
658 pub(super) fn report_borrowed_value_does_not_live_long_enough(
661 borrow
: &BorrowData
<'tcx
>,
662 place_span
: (&Place
<'tcx
>, Span
),
663 kind
: Option
<WriteKind
>,
666 "report_borrowed_value_does_not_live_long_enough(\
667 {:?}, {:?}, {:?}, {:?}\
669 context
, borrow
, place_span
, kind
672 let drop_span
= place_span
.1;
673 let scope_tree
= self.infcx
.tcx
.region_scope_tree(self.mir_def_id
);
674 let root_place
= self.prefixes(&borrow
.borrowed_place
, PrefixSet
::All
)
678 let borrow_spans
= self.retrieve_borrow_spans(borrow
);
679 let borrow_span
= borrow_spans
.var_or_use();
681 let proper_span
= match *root_place
{
682 Place
::Local(local
) => self.mir
.local_decls
[local
].source_info
.span
,
686 if self.access_place_error_reported
687 .contains(&(root_place
.clone(), borrow_span
))
690 "suppressing access_place error when borrow doesn't live long enough for {:?}",
696 self.access_place_error_reported
697 .insert((root_place
.clone(), borrow_span
));
699 if let StorageDeadOrDrop
::Destructor(dropped_ty
) =
700 self.classify_drop_access_kind(&borrow
.borrowed_place
)
702 // If a borrow of path `B` conflicts with drop of `D` (and
703 // we're not in the uninteresting case where `B` is a
704 // prefix of `D`), then report this as a more interesting
705 // destructor conflict.
706 if !borrow
.borrowed_place
.is_prefix_of(place_span
.0) {
707 self.report_borrow_conflicts_with_destructor(
708 context
, borrow
, place_span
, kind
, dropped_ty
,
714 let place_desc
= self.describe_place(&borrow
.borrowed_place
);
716 let kind_place
= kind
.filter(|_
| place_desc
.is_some()).map(|k
| (k
, place_span
.0));
717 let explanation
= self.explain_why_borrow_contains_point(context
, &borrow
, kind_place
);
719 let err
= match (place_desc
, explanation
) {
720 (Some(_
), _
) if self.is_place_thread_local(root_place
) => {
721 self.report_thread_local_value_does_not_live_long_enough(drop_span
, borrow_span
)
723 // If the outlives constraint comes from inside the closure,
728 // Box::new(|| y) as Box<Fn() -> &'static i32>
730 // then just use the normal error. The closure isn't escaping
731 // and `move` will not help here.
734 BorrowExplanation
::MustBeValidFor
{
735 category
: category @ ConstraintCategory
::Return
,
744 BorrowExplanation
::MustBeValidFor
{
745 category
: category @ ConstraintCategory
::CallArgument
,
751 ) if borrow_spans
.for_closure() => self.report_escaping_closure_capture(
752 borrow_spans
.args_or_use(),
757 &format
!("`{}`", name
),
761 BorrowExplanation
::MustBeValidFor
{
762 category
: ConstraintCategory
::Assignment
,
764 region_name
: RegionName
{
765 source
: RegionNameSource
::AnonRegionFromUpvar(upvar_span
, ref upvar_name
),
771 ) => self.report_escaping_data(borrow_span
, name
, upvar_span
, upvar_name
, span
),
772 (Some(name
), explanation
) => self.report_local_value_does_not_live_long_enough(
781 (None
, explanation
) => self.report_temporary_value_does_not_live_long_enough(
792 err
.buffer(&mut self.errors_buffer
);
795 fn report_local_value_does_not_live_long_enough(
799 scope_tree
: &Lrc
<ScopeTree
>,
800 borrow
: &BorrowData
<'tcx
>,
802 borrow_spans
: UseSpans
,
803 explanation
: BorrowExplanation
,
804 ) -> DiagnosticBuilder
<'cx
> {
806 "report_local_value_does_not_live_long_enough(\
807 {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
809 context
, name
, scope_tree
, borrow
, drop_span
, borrow_spans
812 let borrow_span
= borrow_spans
.var_or_use();
813 if let BorrowExplanation
::MustBeValidFor
{
814 category
: ConstraintCategory
::Return
,
820 return self.report_cannot_return_reference_to_local(
824 opt_place_desc
.as_ref(),
828 let mut err
= self.infcx
.tcx
.path_does_not_live_long_enough(
830 &format
!("`{}`", name
),
834 if let Some(annotation
) = self.annotate_argument_and_return_for_borrow(borrow
) {
835 let region_name
= annotation
.emit(&mut err
);
839 format
!("`{}` would have to be valid for `{}`...", name
, region_name
),
842 if let Some(fn_hir_id
) = self.infcx
.tcx
.hir().as_local_hir_id(self.mir_def_id
) {
846 "...but `{}` will be dropped here, when the function `{}` returns",
848 self.infcx
.tcx
.hir().name_by_hir_id(fn_hir_id
),
853 "functions cannot return a borrow to data owned within the function's scope, \
854 functions can only return borrows to data passed as arguments",
857 "to learn more, visit <https://doc.rust-lang.org/book/ch04-02-\
858 references-and-borrowing.html#dangling-references>",
863 format
!("...but `{}` dropped here while still borrowed", name
),
867 if let BorrowExplanation
::MustBeValidFor { .. }
= explanation
{
869 explanation
.add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
872 err
.span_label(borrow_span
, "borrowed value does not live long enough");
875 format
!("`{}` dropped here while still borrowed", name
),
878 let within
= if borrow_spans
.for_generator() {
884 borrow_spans
.args_span_label(
886 format
!("value captured here{}", within
),
889 explanation
.add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
895 fn report_borrow_conflicts_with_destructor(
898 borrow
: &BorrowData
<'tcx
>,
899 (place
, drop_span
): (&Place
<'tcx
>, Span
),
900 kind
: Option
<WriteKind
>,
901 dropped_ty
: ty
::Ty
<'tcx
>,
904 "report_borrow_conflicts_with_destructor(\
905 {:?}, {:?}, ({:?}, {:?}), {:?}\
907 context
, borrow
, place
, drop_span
, kind
,
910 let borrow_spans
= self.retrieve_borrow_spans(borrow
);
911 let borrow_span
= borrow_spans
.var_or_use();
913 let mut err
= self.infcx
915 .cannot_borrow_across_destructor(borrow_span
, Origin
::Mir
);
917 let what_was_dropped
= match self.describe_place(place
) {
918 Some(name
) => format
!("`{}`", name
.as_str()),
919 None
=> String
::from("temporary value"),
922 let label
= match self.describe_place(&borrow
.borrowed_place
) {
923 Some(borrowed
) => format
!(
924 "here, drop of {D} needs exclusive access to `{B}`, \
925 because the type `{T}` implements the `Drop` trait",
926 D
= what_was_dropped
,
931 "here is drop of {D}; whose type `{T}` implements the `Drop` trait",
932 D
= what_was_dropped
,
936 err
.span_label(drop_span
, label
);
938 // Only give this note and suggestion if they could be relevant.
940 self.explain_why_borrow_contains_point(context
, borrow
, kind
.map(|k
| (k
, place
)));
942 BorrowExplanation
::UsedLater { .. }
943 | BorrowExplanation
::UsedLaterWhenDropped { .. }
=> {
944 err
.note("consider using a `let` binding to create a longer lived value");
949 explanation
.add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
951 err
.buffer(&mut self.errors_buffer
);
954 fn report_thread_local_value_does_not_live_long_enough(
958 ) -> DiagnosticBuilder
<'cx
> {
960 "report_thread_local_value_does_not_live_long_enough(\
963 drop_span
, borrow_span
966 let mut err
= self.infcx
968 .thread_local_value_does_not_live_long_enough(borrow_span
, Origin
::Mir
);
972 "thread-local variables cannot be borrowed beyond the end of the function",
974 err
.span_label(drop_span
, "end of enclosing function is here");
979 fn report_temporary_value_does_not_live_long_enough(
982 scope_tree
: &Lrc
<ScopeTree
>,
983 borrow
: &BorrowData
<'tcx
>,
985 borrow_spans
: UseSpans
,
987 explanation
: BorrowExplanation
,
988 ) -> DiagnosticBuilder
<'cx
> {
990 "report_temporary_value_does_not_live_long_enough(\
991 {:?}, {:?}, {:?}, {:?}, {:?}\
993 context
, scope_tree
, borrow
, drop_span
, proper_span
996 if let BorrowExplanation
::MustBeValidFor
{
997 category
: ConstraintCategory
::Return
,
1002 return self.report_cannot_return_reference_to_local(
1010 let tcx
= self.infcx
.tcx
;
1011 let mut err
= tcx
.temporary_value_borrowed_for_too_long(proper_span
, Origin
::Mir
);
1014 "creates a temporary which is freed while still in use",
1018 "temporary value is freed at the end of this statement",
1022 BorrowExplanation
::UsedLater(..)
1023 | BorrowExplanation
::UsedLaterInLoop(..)
1024 | BorrowExplanation
::UsedLaterWhenDropped { .. }
=> {
1025 // Only give this note and suggestion if it could be relevant.
1026 err
.note("consider using a `let` binding to create a longer lived value");
1030 explanation
.add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
1032 let within
= if borrow_spans
.for_generator() {
1038 borrow_spans
.args_span_label(
1040 format
!("value captured here{}", within
),
1046 fn report_cannot_return_reference_to_local(
1048 borrow
: &BorrowData
<'tcx
>,
1051 opt_place_desc
: Option
<&String
>,
1052 ) -> DiagnosticBuilder
<'cx
> {
1053 let tcx
= self.infcx
.tcx
;
1055 // FIXME use a better heuristic than Spans
1056 let reference_desc
= if return_span
== self.mir
.source_info(borrow
.reserve_location
).span
{
1062 let (place_desc
, note
) = if let Some(place_desc
) = opt_place_desc
{
1063 let local_kind
= match borrow
.borrowed_place
{
1064 Place
::Local(local
) => {
1065 match self.mir
.local_kind(local
) {
1066 LocalKind
::ReturnPointer
1067 | LocalKind
::Temp
=> bug
!("temporary or return pointer with a name"),
1068 LocalKind
::Var
=> "local variable ",
1070 if !self.mir
.upvar_decls
.is_empty()
1071 && local
== Local
::new(1) => {
1072 "variable captured by `move` "
1075 "function parameter "
1082 format
!("{}`{}`", local_kind
, place_desc
),
1083 format
!("`{}` is borrowed here", place_desc
),
1086 let root_place
= self.prefixes(&borrow
.borrowed_place
, PrefixSet
::All
)
1089 let local
= if let Place
::Local(local
) = *root_place
{
1092 bug
!("report_cannot_return_reference_to_local: not a local")
1094 match self.mir
.local_kind(local
) {
1095 LocalKind
::ReturnPointer
| LocalKind
::Temp
=> {
1097 "temporary value".to_string(),
1098 "temporary value created here".to_string(),
1103 "function parameter".to_string(),
1104 "function parameter borrowed here".to_string(),
1107 LocalKind
::Var
=> bug
!("local variable without a name"),
1111 let mut err
= tcx
.cannot_return_reference_to_local(
1118 if return_span
!= borrow_span
{
1119 err
.span_label(borrow_span
, note
);
1125 fn report_escaping_closure_capture(
1129 fr_name
: &RegionName
,
1130 category
: ConstraintCategory
,
1131 constraint_span
: Span
,
1133 ) -> DiagnosticBuilder
<'cx
> {
1134 let tcx
= self.infcx
.tcx
;
1136 let mut err
= tcx
.cannot_capture_in_long_lived_closure(
1143 let suggestion
= match tcx
.sess
.source_map().span_to_snippet(args_span
) {
1144 Ok(string
) => format
!("move {}", string
),
1145 Err(_
) => "move |<args>| <body>".to_string()
1148 err
.span_suggestion(
1150 &format
!("to force the closure to take ownership of {} (and any \
1151 other referenced variables), use the `move` keyword",
1154 Applicability
::MachineApplicable
,
1158 ConstraintCategory
::Return
=> {
1159 err
.span_note(constraint_span
, "closure is returned here");
1161 ConstraintCategory
::CallArgument
=> {
1162 fr_name
.highlight_region_name(&mut err
);
1165 &format
!("function requires argument type to outlive `{}`", fr_name
),
1168 _
=> bug
!("report_escaping_closure_capture called with unexpected constraint \
1169 category: `{:?}`", category
),
1174 fn report_escaping_data(
1177 name
: &Option
<String
>,
1181 ) -> DiagnosticBuilder
<'cx
> {
1182 let tcx
= self.infcx
.tcx
;
1184 let escapes_from
= if tcx
.is_closure(self.mir_def_id
) {
1185 let tables
= tcx
.typeck_tables_of(self.mir_def_id
);
1186 let mir_hir_id
= tcx
.hir().def_index_to_hir_id(self.mir_def_id
.index
);
1187 match tables
.node_type(mir_hir_id
).sty
{
1188 ty
::Closure(..) => "closure",
1189 ty
::Generator(..) => "generator",
1190 _
=> bug
!("Closure body doesn't have a closure or generator type"),
1196 let mut err
= tcx
.borrowed_data_escapes_closure(escape_span
, escapes_from
, Origin
::Mir
);
1201 "`{}` is declared here, outside of the {} body",
1202 upvar_name
, escapes_from
1209 "borrow is only valid in the {} body",
1214 if let Some(name
) = name
{
1217 format
!("reference to `{}` escapes the {} body here", name
, escapes_from
),
1222 format
!("reference escapes the {} body here", escapes_from
),
1229 fn get_moved_indexes(&mut self, context
: Context
, mpi
: MovePathIndex
) -> Vec
<MoveSite
> {
1232 let mut stack
= Vec
::new();
1233 stack
.extend(mir
.predecessor_locations(context
.loc
).map(|predecessor
| {
1234 let is_back_edge
= context
.loc
.dominates(predecessor
, &self.dominators
);
1235 (predecessor
, is_back_edge
)
1238 let mut visited
= FxHashSet
::default();
1239 let mut result
= vec
![];
1241 'dfs
: while let Some((location
, is_back_edge
)) = stack
.pop() {
1243 "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
1244 location
, is_back_edge
1247 if !visited
.insert(location
) {
1252 let stmt_kind
= mir
[location
.block
]
1254 .get(location
.statement_index
)
1256 if let Some(StatementKind
::StorageDead(..)) = stmt_kind
{
1257 // this analysis only tries to find moves explicitly
1258 // written by the user, so we ignore the move-outs
1259 // created by `StorageDead` and at the beginning
1262 // If we are found a use of a.b.c which was in error, then we want to look for
1263 // moves not only of a.b.c but also a.b and a.
1265 // Note that the moves data already includes "parent" paths, so we don't have to
1266 // worry about the other case: that is, if there is a move of a.b.c, it is already
1267 // marked as a move of a.b and a as well, so we will generate the correct errors
1269 let mut mpis
= vec
![mpi
];
1270 let move_paths
= &self.move_data
.move_paths
;
1271 mpis
.extend(move_paths
[mpi
].parents(move_paths
));
1273 for moi
in &self.move_data
.loc_map
[location
] {
1274 debug
!("report_use_of_moved_or_uninitialized: moi={:?}", moi
);
1275 if mpis
.contains(&self.move_data
.moves
[*moi
].path
) {
1276 debug
!("report_use_of_moved_or_uninitialized: found");
1277 result
.push(MoveSite
{
1279 traversed_back_edge
: is_back_edge
,
1282 // Strictly speaking, we could continue our DFS here. There may be
1283 // other moves that can reach the point of error. But it is kind of
1284 // confusing to highlight them.
1292 // drop(a); // <-- current point of error
1295 // Because we stop the DFS here, we only highlight `let c = a`,
1296 // and not `let b = a`. We will of course also report an error at
1297 // `let c = a` which highlights `let b = a` as the move.
1304 let mut any_match
= false;
1305 drop_flag_effects
::for_location_inits(
1320 stack
.extend(mir
.predecessor_locations(location
).map(|predecessor
| {
1321 let back_edge
= location
.dominates(predecessor
, &self.dominators
);
1322 (predecessor
, is_back_edge
|| back_edge
)
1329 pub(super) fn report_illegal_mutation_of_borrowed(
1332 (place
, span
): (&Place
<'tcx
>, Span
),
1333 loan
: &BorrowData
<'tcx
>,
1335 let loan_spans
= self.retrieve_borrow_spans(loan
);
1336 let loan_span
= loan_spans
.args_or_use();
1338 let tcx
= self.infcx
.tcx
;
1339 if loan
.kind
== BorrowKind
::Shallow
{
1340 let mut err
= tcx
.cannot_mutate_in_match_guard(
1343 &self.describe_place(place
).unwrap_or_else(|| "_".to_owned()),
1347 loan_spans
.var_span_label(
1349 format
!("borrow occurs due to use{}", loan_spans
.describe()),
1352 err
.buffer(&mut self.errors_buffer
);
1357 let mut err
= tcx
.cannot_assign_to_borrowed(
1360 &self.describe_place(place
).unwrap_or_else(|| "_".to_owned()),
1364 loan_spans
.var_span_label(
1366 format
!("borrow occurs due to use{}", loan_spans
.describe()),
1369 self.explain_why_borrow_contains_point(context
, loan
, None
)
1370 .add_explanation_to_diagnostic(self.infcx
.tcx
, self.mir
, &mut err
, "");
1372 err
.buffer(&mut self.errors_buffer
);
1375 /// Reports an illegal reassignment; for example, an assignment to
1376 /// (part of) a non-`mut` local that occurs potentially after that
1377 /// local has already been initialized. `place` is the path being
1378 /// assigned; `err_place` is a place providing a reason why
1379 /// `place` is not mutable (e.g., the non-`mut` local `x` in an
1380 /// assignment to `x.f`).
1381 pub(super) fn report_illegal_reassignment(
1384 (place
, span
): (&Place
<'tcx
>, Span
),
1385 assigned_span
: Span
,
1386 err_place
: &Place
<'tcx
>,
1388 let (from_arg
, local_decl
) = if let Place
::Local(local
) = *err_place
{
1389 if let LocalKind
::Arg
= self.mir
.local_kind(local
) {
1390 (true, Some(&self.mir
.local_decls
[local
]))
1392 (false, Some(&self.mir
.local_decls
[local
]))
1398 // If root local is initialized immediately (everything apart from let
1399 // PATTERN;) then make the error refer to that local, rather than the
1400 // place being assigned later.
1401 let (place_description
, assigned_span
) = match local_decl
{
1403 is_user_variable
: Some(ClearCrossCrate
::Clear
),
1408 Some(ClearCrossCrate
::Set(BindingForm
::Var(VarBindingForm
{
1409 opt_match_place
: None
,
1415 is_user_variable
: None
,
1418 | None
=> (self.describe_place(place
), assigned_span
),
1419 Some(decl
) => (self.describe_place(err_place
), decl
.source_info
.span
),
1422 let mut err
= self.infcx
.tcx
.cannot_reassign_immutable(
1424 place_description
.as_ref().map(AsRef
::as_ref
).unwrap_or("_"),
1428 let msg
= if from_arg
{
1429 "cannot assign to immutable argument"
1431 "cannot assign twice to immutable variable"
1433 if span
!= assigned_span
{
1435 let value_msg
= match place_description
{
1436 Some(name
) => format
!("`{}`", name
),
1437 None
=> "value".to_owned(),
1439 err
.span_label(assigned_span
, format
!("first assignment to {}", value_msg
));
1442 if let Some(decl
) = local_decl
{
1443 if let Some(name
) = decl
.name
{
1444 if decl
.can_be_made_mutable() {
1445 err
.span_suggestion(
1446 decl
.source_info
.span
,
1447 "make this binding mutable",
1448 format
!("mut {}", name
),
1449 Applicability
::MachineApplicable
,
1454 err
.span_label(span
, msg
);
1455 err
.buffer(&mut self.errors_buffer
);
1459 pub(super) struct IncludingDowncast(bool
);
1461 /// Which case a StorageDeadOrDrop is for.
1462 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1463 enum StorageDeadOrDrop
<'tcx
> {
1466 Destructor(ty
::Ty
<'tcx
>),
1469 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
1471 /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
1472 /// is moved after being invoked.
1475 /// note: closure cannot be invoked more than once because it moves the variable `dict` out of
1477 /// --> $DIR/issue-42065.rs:16:29
1479 /// LL | for (key, value) in dict {
1482 pub(super) fn add_moved_or_invoked_closure_note(
1485 place
: &Place
<'tcx
>,
1486 diag
: &mut DiagnosticBuilder
<'_
>,
1488 debug
!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location
, place
);
1489 let mut target
= place
.local();
1490 for stmt
in &self.mir
[location
.block
].statements
[location
.statement_index
..] {
1491 debug
!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt
, target
);
1492 if let StatementKind
::Assign(into
, box Rvalue
::Use(from
)) = &stmt
.kind
{
1493 debug
!("add_fnonce_closure_note: into={:?} from={:?}", into
, from
);
1495 Operand
::Copy(ref place
) |
1496 Operand
::Move(ref place
) if target
== place
.local() =>
1497 target
= into
.local(),
1503 // Check if we are attempting to call a closure after it has been invoked.
1504 let terminator
= self.mir
[location
.block
].terminator();
1505 debug
!("add_moved_or_invoked_closure_note: terminator={:?}", terminator
);
1506 if let TerminatorKind
::Call
{
1507 func
: Operand
::Constant(box Constant
{
1508 literal
: ty
::LazyConst
::Evaluated(ty
::Const
{
1509 ty
: &ty
::TyS { sty: ty::TyKind::FnDef(id, _), .. }
,
1516 } = &terminator
.kind
{
1517 debug
!("add_moved_or_invoked_closure_note: id={:?}", id
);
1518 if self.infcx
.tcx
.parent(id
) == self.infcx
.tcx
.lang_items().fn_once_trait() {
1519 let closure
= match args
.first() {
1520 Some(Operand
::Copy(ref place
)) |
1521 Some(Operand
::Move(ref place
)) if target
== place
.local() =>
1522 place
.local().unwrap(),
1526 debug
!("add_moved_or_invoked_closure_note: closure={:?}", closure
);
1527 if let ty
::TyKind
::Closure(did
, _
) = self.mir
.local_decls
[closure
].ty
.sty
{
1528 let node_id
= self.infcx
.tcx
.hir().as_local_node_id(did
).unwrap();
1529 let hir_id
= self.infcx
.tcx
.hir().node_to_hir_id(node_id
);
1531 if let Some((span
, name
)) = self.infcx
.tcx
.typeck_tables_of(did
)
1532 .closure_kind_origins()
1538 "closure cannot be invoked more than once because it moves the \
1539 variable `{}` out of its environment",
1549 // Check if we are just moving a closure after it has been invoked.
1550 if let Some(target
) = target
{
1551 if let ty
::TyKind
::Closure(did
, _
) = self.mir
.local_decls
[target
].ty
.sty
{
1552 let node_id
= self.infcx
.tcx
.hir().as_local_node_id(did
).unwrap();
1553 let hir_id
= self.infcx
.tcx
.hir().node_to_hir_id(node_id
);
1555 if let Some((span
, name
)) = self.infcx
.tcx
.typeck_tables_of(did
)
1556 .closure_kind_origins()
1562 "closure cannot be moved more than once as it is not `Copy` due to \
1563 moving the variable `{}` out of its environment",
1572 /// End-user visible description of `place` if one can be found. If the
1573 /// place is a temporary for instance, None will be returned.
1574 pub(super) fn describe_place(&self, place
: &Place
<'tcx
>) -> Option
<String
> {
1575 self.describe_place_with_options(place
, IncludingDowncast(false))
1578 /// End-user visible description of `place` if one can be found. If the
1579 /// place is a temporary for instance, None will be returned.
1580 /// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
1581 /// `Downcast` and `IncludingDowncast` is true
1582 pub(super) fn describe_place_with_options(
1584 place
: &Place
<'tcx
>,
1585 including_downcast
: IncludingDowncast
,
1586 ) -> Option
<String
> {
1587 let mut buf
= String
::new();
1588 match self.append_place_to_string(place
, &mut buf
, false, &including_downcast
) {
1589 Ok(()) => Some(buf
),
1594 /// Appends end-user visible description of `place` to `buf`.
1595 fn append_place_to_string(
1597 place
: &Place
<'tcx
>,
1599 mut autoderef
: bool
,
1600 including_downcast
: &IncludingDowncast
,
1601 ) -> Result
<(), ()> {
1603 Place
::Promoted(_
) => {
1604 buf
.push_str("promoted");
1606 Place
::Local(local
) => {
1607 self.append_local_to_string(local
, buf
)?
;
1609 Place
::Static(ref static_
) => {
1610 buf
.push_str(&self.infcx
.tcx
.item_name(static_
.def_id
).to_string());
1612 Place
::Projection(ref proj
) => {
1614 ProjectionElem
::Deref
=> {
1615 let upvar_field_projection
=
1616 place
.is_upvar_field_projection(self.mir
, &self.infcx
.tcx
);
1617 if let Some(field
) = upvar_field_projection
{
1618 let var_index
= field
.index();
1619 let name
= self.mir
.upvar_decls
[var_index
].debug_name
.to_string();
1620 if self.mir
.upvar_decls
[var_index
].by_ref
{
1621 buf
.push_str(&name
);
1623 buf
.push_str(&format
!("*{}", &name
));
1627 self.append_place_to_string(
1631 &including_downcast
,
1633 } else if let Place
::Local(local
) = proj
.base
{
1634 if let Some(ClearCrossCrate
::Set(BindingForm
::RefForGuard
)) =
1635 self.mir
.local_decls
[local
].is_user_variable
1637 self.append_place_to_string(
1641 &including_downcast
,
1645 self.append_place_to_string(
1649 &including_downcast
,
1654 self.append_place_to_string(
1658 &including_downcast
,
1663 ProjectionElem
::Downcast(..) => {
1664 self.append_place_to_string(
1668 &including_downcast
,
1670 if including_downcast
.0 {
1674 ProjectionElem
::Field(field
, _ty
) => {
1677 let upvar_field_projection
=
1678 place
.is_upvar_field_projection(self.mir
, &self.infcx
.tcx
);
1679 if let Some(field
) = upvar_field_projection
{
1680 let var_index
= field
.index();
1681 let name
= self.mir
.upvar_decls
[var_index
].debug_name
.to_string();
1682 buf
.push_str(&name
);
1684 let field_name
= self.describe_field(&proj
.base
, field
);
1685 self.append_place_to_string(
1689 &including_downcast
,
1691 buf
.push_str(&format
!(".{}", field_name
));
1694 ProjectionElem
::Index(index
) => {
1697 self.append_place_to_string(
1701 &including_downcast
,
1704 if self.append_local_to_string(index
, buf
).is_err() {
1709 ProjectionElem
::ConstantIndex { .. }
| ProjectionElem
::Subslice { .. }
=> {
1711 // Since it isn't possible to borrow an element on a particular index and
1712 // then use another while the borrow is held, don't output indices details
1713 // to avoid confusing the end-user
1714 self.append_place_to_string(
1718 &including_downcast
,
1720 buf
.push_str(&"[..]");
1729 /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
1730 /// a name, then `Err` is returned
1731 fn append_local_to_string(&self, local_index
: Local
, buf
: &mut String
) -> Result
<(), ()> {
1732 let local
= &self.mir
.local_decls
[local_index
];
1735 buf
.push_str(&name
.to_string());
1742 /// End-user visible description of the `field`nth field of `base`
1743 fn describe_field(&self, base
: &Place
<'_
>, field
: Field
) -> String
{
1745 Place
::Local(local
) => {
1746 let local
= &self.mir
.local_decls
[local
];
1747 self.describe_field_from_ty(&local
.ty
, field
)
1749 Place
::Promoted(ref prom
) => self.describe_field_from_ty(&prom
.1, field
),
1750 Place
::Static(ref static_
) => self.describe_field_from_ty(&static_
.ty
, field
),
1751 Place
::Projection(ref proj
) => match proj
.elem
{
1752 ProjectionElem
::Deref
=> self.describe_field(&proj
.base
, field
),
1753 ProjectionElem
::Downcast(def
, variant_index
) =>
1754 def
.variants
[variant_index
].fields
[field
.index()].ident
.to_string(),
1755 ProjectionElem
::Field(_
, field_type
) => {
1756 self.describe_field_from_ty(&field_type
, field
)
1758 ProjectionElem
::Index(..)
1759 | ProjectionElem
::ConstantIndex { .. }
1760 | ProjectionElem
::Subslice { .. }
=> {
1761 self.describe_field(&proj
.base
, field
)
1767 /// End-user visible description of the `field_index`nth field of `ty`
1768 fn describe_field_from_ty(&self, ty
: &ty
::Ty
<'_
>, field
: Field
) -> String
{
1770 // If the type is a box, the field is described from the boxed type
1771 self.describe_field_from_ty(&ty
.boxed_ty(), field
)
1774 ty
::Adt(def
, _
) => if def
.is_enum() {
1775 field
.index().to_string()
1777 def
.non_enum_variant().fields
[field
.index()]
1781 ty
::Tuple(_
) => field
.index().to_string(),
1782 ty
::Ref(_
, ty
, _
) | ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) => {
1783 self.describe_field_from_ty(&ty
, field
)
1785 ty
::Array(ty
, _
) | ty
::Slice(ty
) => self.describe_field_from_ty(&ty
, field
),
1786 ty
::Closure(def_id
, _
) | ty
::Generator(def_id
, _
, _
) => {
1787 // Convert the def-id into a node-id. node-ids are only valid for
1788 // the local code in the current crate, so this returns an `Option` in case
1789 // the closure comes from another crate. But in that case we wouldn't
1790 // be borrowck'ing it, so we can just unwrap:
1791 let node_id
= self.infcx
.tcx
.hir().as_local_node_id(def_id
).unwrap();
1792 let freevar
= self.infcx
1794 .with_freevars(node_id
, |fv
| fv
[field
.index()]);
1796 self.infcx
.tcx
.hir().name(freevar
.var_id()).to_string()
1799 // Might need a revision when the fields in trait RFC is implemented
1800 // (https://github.com/rust-lang/rfcs/pull/1546)
1802 "End-user description not implemented for field access on `{:?}`",
1810 /// Checks if a place is a thread-local static.
1811 pub fn is_place_thread_local(&self, place
: &Place
<'tcx
>) -> bool
{
1812 if let Place
::Static(statik
) = place
{
1813 let attrs
= self.infcx
.tcx
.get_attrs(statik
.def_id
);
1814 let is_thread_local
= attrs
.iter().any(|attr
| attr
.check_name("thread_local"));
1817 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
1818 attrs
, is_thread_local
1822 debug
!("is_place_thread_local: no");
1827 fn classify_drop_access_kind(&self, place
: &Place
<'tcx
>) -> StorageDeadOrDrop
<'tcx
> {
1828 let tcx
= self.infcx
.tcx
;
1830 Place
::Local(_
) | Place
::Static(_
) | Place
::Promoted(_
) => {
1831 StorageDeadOrDrop
::LocalStorageDead
1833 Place
::Projection(box PlaceProjection { base, elem }
) => {
1834 let base_access
= self.classify_drop_access_kind(base
);
1836 ProjectionElem
::Deref
=> match base_access
{
1837 StorageDeadOrDrop
::LocalStorageDead
1838 | StorageDeadOrDrop
::BoxedStorageDead
=> {
1840 base
.ty(self.mir
, tcx
).to_ty(tcx
).is_box(),
1841 "Drop of value behind a reference or raw pointer"
1843 StorageDeadOrDrop
::BoxedStorageDead
1845 StorageDeadOrDrop
::Destructor(_
) => base_access
,
1847 ProjectionElem
::Field(..) | ProjectionElem
::Downcast(..) => {
1848 let base_ty
= base
.ty(self.mir
, tcx
).to_ty(tcx
);
1850 ty
::Adt(def
, _
) if def
.has_dtor(tcx
) => {
1851 // Report the outermost adt with a destructor
1853 StorageDeadOrDrop
::Destructor(_
) => base_access
,
1854 StorageDeadOrDrop
::LocalStorageDead
1855 | StorageDeadOrDrop
::BoxedStorageDead
=> {
1856 StorageDeadOrDrop
::Destructor(base_ty
)
1864 ProjectionElem
::ConstantIndex { .. }
1865 | ProjectionElem
::Subslice { .. }
1866 | ProjectionElem
::Index(_
) => base_access
,
1872 /// Annotate argument and return type of function and closure with (synthesized) lifetime for
1873 /// borrow of local value that does not live long enough.
1874 fn annotate_argument_and_return_for_borrow(
1876 borrow
: &BorrowData
<'tcx
>,
1877 ) -> Option
<AnnotatedBorrowFnSignature
<'_
>> {
1878 // Define a fallback for when we can't match a closure.
1880 let is_closure
= self.infcx
.tcx
.is_closure(self.mir_def_id
);
1884 let ty
= self.infcx
.tcx
.type_of(self.mir_def_id
);
1886 ty
::TyKind
::FnDef(_
, _
) | ty
::TyKind
::FnPtr(_
) => self.annotate_fn_sig(
1888 self.infcx
.tcx
.fn_sig(self.mir_def_id
),
1895 // In order to determine whether we need to annotate, we need to check whether the reserve
1896 // place was an assignment into a temporary.
1898 // If it was, we check whether or not that temporary is eventually assigned into the return
1899 // place. If it was, we can add annotations about the function's return type and arguments
1900 // and it'll make sense.
1901 let location
= borrow
.reserve_location
;
1903 "annotate_argument_and_return_for_borrow: location={:?}",
1906 if let Some(&Statement { kind: StatementKind::Assign(ref reservation, _), ..}
)
1907 = &self.mir
[location
.block
].statements
.get(location
.statement_index
)
1910 "annotate_argument_and_return_for_borrow: reservation={:?}",
1913 // Check that the initial assignment of the reserve location is into a temporary.
1914 let mut target
= *match reservation
{
1915 Place
::Local(local
) if self.mir
.local_kind(*local
) == LocalKind
::Temp
=> local
,
1919 // Next, look through the rest of the block, checking if we are assigning the
1920 // `target` (that is, the place that contains our borrow) to anything.
1921 let mut annotated_closure
= None
;
1922 for stmt
in &self.mir
[location
.block
].statements
[location
.statement_index
+ 1..] {
1924 "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
1927 if let StatementKind
::Assign(Place
::Local(assigned_to
), box rvalue
) = &stmt
.kind
1930 "annotate_argument_and_return_for_borrow: assigned_to={:?} \
1934 // Check if our `target` was captured by a closure.
1935 if let Rvalue
::Aggregate(
1936 box AggregateKind
::Closure(def_id
, substs
),
1940 for operand
in operands
{
1941 let assigned_from
= match operand
{
1942 Operand
::Copy(assigned_from
) | Operand
::Move(assigned_from
) => {
1948 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
1952 // Find the local from the operand.
1953 let assigned_from_local
= match assigned_from
.local() {
1954 Some(local
) => local
,
1958 if assigned_from_local
!= target
{
1962 // If a closure captured our `target` and then assigned
1963 // into a place then we should annotate the closure in
1964 // case it ends up being assigned into the return place.
1965 annotated_closure
= self.annotate_fn_sig(
1967 self.infcx
.closure_sig(*def_id
, *substs
),
1970 "annotate_argument_and_return_for_borrow: \
1971 annotated_closure={:?} assigned_from_local={:?} \
1973 annotated_closure
, assigned_from_local
, assigned_to
1976 if *assigned_to
== mir
::RETURN_PLACE
{
1977 // If it was assigned directly into the return place, then
1979 return annotated_closure
;
1981 // Otherwise, update the target.
1982 target
= *assigned_to
;
1986 // If none of our closure's operands matched, then skip to the next
1991 // Otherwise, look at other types of assignment.
1992 let assigned_from
= match rvalue
{
1993 Rvalue
::Ref(_
, _
, assigned_from
) => assigned_from
,
1994 Rvalue
::Use(operand
) => match operand
{
1995 Operand
::Copy(assigned_from
) | Operand
::Move(assigned_from
) => {
2003 "annotate_argument_and_return_for_borrow: \
2004 assigned_from={:?}",
2008 // Find the local from the rvalue.
2009 let assigned_from_local
= match assigned_from
.local() {
2010 Some(local
) => local
,
2014 "annotate_argument_and_return_for_borrow: \
2015 assigned_from_local={:?}",
2016 assigned_from_local
,
2019 // Check if our local matches the target - if so, we've assigned our
2020 // borrow to a new place.
2021 if assigned_from_local
!= target
{
2025 // If we assigned our `target` into a new place, then we should
2026 // check if it was the return place.
2028 "annotate_argument_and_return_for_borrow: \
2029 assigned_from_local={:?} assigned_to={:?}",
2030 assigned_from_local
, assigned_to
2032 if *assigned_to
== mir
::RETURN_PLACE
{
2033 // If it was then return the annotated closure if there was one,
2034 // else, annotate this function.
2035 return annotated_closure
.or_else(fallback
);
2038 // If we didn't assign into the return place, then we just update
2040 target
= *assigned_to
;
2044 // Check the terminator if we didn't find anything in the statements.
2045 let terminator
= &self.mir
[location
.block
].terminator();
2047 "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
2050 if let TerminatorKind
::Call
{
2051 destination
: Some((Place
::Local(assigned_to
), _
)),
2054 } = &terminator
.kind
2057 "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
2060 for operand
in args
{
2061 let assigned_from
= match operand
{
2062 Operand
::Copy(assigned_from
) | Operand
::Move(assigned_from
) => {
2068 "annotate_argument_and_return_for_borrow: assigned_from={:?}",
2072 if let Some(assigned_from_local
) = assigned_from
.local() {
2074 "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
2075 assigned_from_local
,
2078 if *assigned_to
== mir
::RETURN_PLACE
&& assigned_from_local
== target
{
2079 return annotated_closure
.or_else(fallback
);
2086 // If we haven't found an assignment into the return place, then we need not add
2088 debug
!("annotate_argument_and_return_for_borrow: none found");
2092 /// Annotate the first argument and return type of a function signature if they are
2097 sig
: ty
::PolyFnSig
<'tcx
>,
2098 ) -> Option
<AnnotatedBorrowFnSignature
<'_
>> {
2099 debug
!("annotate_fn_sig: did={:?} sig={:?}", did
, sig
);
2100 let is_closure
= self.infcx
.tcx
.is_closure(did
);
2101 let fn_node_id
= self.infcx
.tcx
.hir().as_local_node_id(did
)?
;
2102 let fn_decl
= self.infcx
.tcx
.hir().fn_decl(fn_node_id
)?
;
2104 // We need to work out which arguments to highlight. We do this by looking
2105 // at the return type, where there are three cases:
2107 // 1. If there are named arguments, then we should highlight the return type and
2108 // highlight any of the arguments that are also references with that lifetime.
2109 // If there are no arguments that have the same lifetime as the return type,
2110 // then don't highlight anything.
2111 // 2. The return type is a reference with an anonymous lifetime. If this is
2112 // the case, then we can take advantage of (and teach) the lifetime elision
2115 // We know that an error is being reported. So the arguments and return type
2116 // must satisfy the elision rules. Therefore, if there is a single argument
2117 // then that means the return type and first (and only) argument have the same
2118 // lifetime and the borrow isn't meeting that, we can highlight the argument
2121 // If there are multiple arguments then the first argument must be self (else
2122 // it would not satisfy the elision rules), so we can highlight self and the
2124 // 3. The return type is not a reference. In this case, we don't highlight
2126 let return_ty
= sig
.output();
2127 match return_ty
.skip_binder().sty
{
2128 ty
::TyKind
::Ref(return_region
, _
, _
) if return_region
.has_name() && !is_closure
=> {
2129 // This is case 1 from above, return type is a named reference so we need to
2130 // search for relevant arguments.
2131 let mut arguments
= Vec
::new();
2132 for (index
, argument
) in sig
.inputs().skip_binder().iter().enumerate() {
2133 if let ty
::TyKind
::Ref(argument_region
, _
, _
) = argument
.sty
{
2134 if argument_region
== return_region
{
2135 // Need to use the `rustc::ty` types to compare against the
2136 // `return_region`. Then use the `rustc::hir` type to get only
2137 // the lifetime span.
2138 if let hir
::TyKind
::Rptr(lifetime
, _
) = &fn_decl
.inputs
[index
].node
{
2139 // With access to the lifetime, we can get
2141 arguments
.push((*argument
, lifetime
.span
));
2143 bug
!("ty type is a ref but hir type is not");
2149 // We need to have arguments. This shouldn't happen, but it's worth checking.
2150 if arguments
.is_empty() {
2154 // We use a mix of the HIR and the Ty types to get information
2155 // as the HIR doesn't have full types for closure arguments.
2156 let return_ty
= *sig
.output().skip_binder();
2157 let mut return_span
= fn_decl
.output
.span();
2158 if let hir
::FunctionRetTy
::Return(ty
) = fn_decl
.output
{
2159 if let hir
::TyKind
::Rptr(lifetime
, _
) = ty
.into_inner().node
{
2160 return_span
= lifetime
.span
;
2164 Some(AnnotatedBorrowFnSignature
::NamedFunction
{
2170 ty
::TyKind
::Ref(_
, _
, _
) if is_closure
=> {
2171 // This is case 2 from above but only for closures, return type is anonymous
2172 // reference so we select
2173 // the first argument.
2174 let argument_span
= fn_decl
.inputs
.first()?
.span
;
2175 let argument_ty
= sig
.inputs().skip_binder().first()?
;
2177 // Closure arguments are wrapped in a tuple, so we need to get the first
2179 if let ty
::TyKind
::Tuple(elems
) = argument_ty
.sty
{
2180 let argument_ty
= elems
.first()?
;
2181 if let ty
::TyKind
::Ref(_
, _
, _
) = argument_ty
.sty
{
2182 return Some(AnnotatedBorrowFnSignature
::Closure
{
2191 ty
::TyKind
::Ref(_
, _
, _
) => {
2192 // This is also case 2 from above but for functions, return type is still an
2193 // anonymous reference so we select the first argument.
2194 let argument_span
= fn_decl
.inputs
.first()?
.span
;
2195 let argument_ty
= sig
.inputs().skip_binder().first()?
;
2197 let return_span
= fn_decl
.output
.span();
2198 let return_ty
= *sig
.output().skip_binder();
2200 // We expect the first argument to be a reference.
2201 match argument_ty
.sty
{
2202 ty
::TyKind
::Ref(_
, _
, _
) => {}
2206 Some(AnnotatedBorrowFnSignature
::AnonymousFunction
{
2214 // This is case 3 from above, return type is not a reference so don't highlight
2223 enum AnnotatedBorrowFnSignature
<'tcx
> {
2225 arguments
: Vec
<(ty
::Ty
<'tcx
>, Span
)>,
2226 return_ty
: ty
::Ty
<'tcx
>,
2230 argument_ty
: ty
::Ty
<'tcx
>,
2231 argument_span
: Span
,
2232 return_ty
: ty
::Ty
<'tcx
>,
2236 argument_ty
: ty
::Ty
<'tcx
>,
2237 argument_span
: Span
,
2241 impl<'tcx
> AnnotatedBorrowFnSignature
<'tcx
> {
2242 /// Annotate the provided diagnostic with information about borrow from the fn signature that
2244 fn emit(&self, diag
: &mut DiagnosticBuilder
<'_
>) -> String
{
2246 AnnotatedBorrowFnSignature
::Closure
{
2252 format
!("has type `{}`", self.get_name_for_ty(argument_ty
, 0)),
2255 self.get_region_name_for_ty(argument_ty
, 0)
2257 AnnotatedBorrowFnSignature
::AnonymousFunction
{
2263 let argument_ty_name
= self.get_name_for_ty(argument_ty
, 0);
2264 diag
.span_label(*argument_span
, format
!("has type `{}`", argument_ty_name
));
2266 let return_ty_name
= self.get_name_for_ty(return_ty
, 0);
2267 let types_equal
= return_ty_name
== argument_ty_name
;
2272 if types_equal { "also " }
else { "" }
,
2278 "argument and return type have the same lifetime due to lifetime elision rules",
2281 "to learn more, visit <https://doc.rust-lang.org/book/ch10-03-\
2282 lifetime-syntax.html#lifetime-elision>",
2285 self.get_region_name_for_ty(return_ty
, 0)
2287 AnnotatedBorrowFnSignature
::NamedFunction
{
2292 // Region of return type and arguments checked to be the same earlier.
2293 let region_name
= self.get_region_name_for_ty(return_ty
, 0);
2294 for (_
, argument_span
) in arguments
{
2295 diag
.span_label(*argument_span
, format
!("has lifetime `{}`", region_name
));
2300 format
!("also has lifetime `{}`", region_name
,),
2304 "use data from the highlighted arguments which match the `{}` lifetime of \
2314 /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
2315 /// name where required.
2316 fn get_name_for_ty(&self, ty
: ty
::Ty
<'tcx
>, counter
: usize) -> String
{
2317 // We need to add synthesized lifetimes where appropriate. We do
2318 // this by hooking into the pretty printer and telling it to label the
2319 // lifetimes without names with the value `'0`.
2321 ty
::TyKind
::Ref(ty
::RegionKind
::ReLateBound(_
, br
), _
, _
)
2323 ty
::RegionKind
::RePlaceholder(ty
::PlaceholderRegion { name: br, .. }
),
2326 ) => RegionHighlightMode
::highlighting_bound_region(*br
, counter
, || ty
.to_string()),
2327 _
=> ty
.to_string(),
2331 /// Returns the name of the provided `Ty` (that must be a reference)'s region with a
2332 /// synthesized lifetime name where required.
2333 fn get_region_name_for_ty(&self, ty
: ty
::Ty
<'tcx
>, counter
: usize) -> String
{
2335 ty
::TyKind
::Ref(region
, _
, _
) => match region
{
2336 ty
::RegionKind
::ReLateBound(_
, br
)
2337 | ty
::RegionKind
::RePlaceholder(ty
::PlaceholderRegion { name: br, .. }
) => {
2338 RegionHighlightMode
::highlighting_bound_region(
2341 || region
.to_string(),
2344 _
=> region
.to_string(),
2346 _
=> bug
!("ty for annotation of borrow region is not a reference"),
2351 // The span(s) associated to a use of a place.
2352 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
2353 pub(super) enum UseSpans
{
2354 // The access is caused by capturing a variable for a closure.
2356 // This is true if the captured variable was from a generator.
2358 // The span of the args of the closure, including the `move` keyword if
2361 // The span of the first use of the captured variable inside the closure.
2364 // This access has a single span associated to it: common case.
2369 pub(super) fn args_or_use(self) -> Span
{
2371 UseSpans
::ClosureUse
{
2374 | UseSpans
::OtherUse(span
) => span
,
2378 pub(super) fn var_or_use(self) -> Span
{
2380 UseSpans
::ClosureUse { var_span: span, .. }
| UseSpans
::OtherUse(span
) => span
,
2384 // Add a span label to the arguments of the closure, if it exists.
2385 pub(super) fn args_span_label(
2387 err
: &mut DiagnosticBuilder
<'_
>,
2388 message
: impl Into
<String
>,
2390 if let UseSpans
::ClosureUse { args_span, .. }
= self {
2391 err
.span_label(args_span
, message
);
2395 // Add a span label to the use of the captured variable, if it exists.
2396 pub(super) fn var_span_label(
2398 err
: &mut DiagnosticBuilder
<'_
>,
2399 message
: impl Into
<String
>,
2401 if let UseSpans
::ClosureUse { var_span, .. }
= self {
2402 err
.span_label(var_span
, message
);
2406 /// Returns `false` if this place is not used in a closure.
2407 fn for_closure(&self) -> bool
{
2409 UseSpans
::ClosureUse { is_generator, .. }
=> !is_generator
,
2414 /// Returns `false` if this place is not used in a generator.
2415 fn for_generator(&self) -> bool
{
2417 UseSpans
::ClosureUse { is_generator, .. }
=> is_generator
,
2422 /// Describe the span associated with a use of a place.
2423 fn describe(&self) -> String
{
2425 UseSpans
::ClosureUse { is_generator, .. }
=> if is_generator
{
2426 " in generator".to_string()
2428 " in closure".to_string()
2430 _
=> "".to_string(),
2434 pub(super) fn or_else
<F
>(self, if_other
: F
) -> Self
2436 F
: FnOnce() -> Self,
2439 closure @ UseSpans
::ClosureUse { .. }
=> closure
,
2440 UseSpans
::OtherUse(_
) => if_other(),
2445 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
2446 /// Finds the spans associated to a move or copy of move_place at location.
2447 pub(super) fn move_spans(
2449 moved_place
: &Place
<'tcx
>, // Could also be an upvar.
2452 use self::UseSpans
::*;
2454 let stmt
= match self.mir
[location
.block
].statements
.get(location
.statement_index
) {
2456 None
=> return OtherUse(self.mir
.source_info(location
).span
),
2459 debug
!("move_spans: moved_place={:?} location={:?} stmt={:?}", moved_place
, location
, stmt
);
2460 if let StatementKind
::Assign(
2462 box Rvalue
::Aggregate(ref kind
, ref places
)
2464 let (def_id
, is_generator
) = match kind
{
2465 box AggregateKind
::Closure(def_id
, _
) => (def_id
, false),
2466 box AggregateKind
::Generator(def_id
, _
, _
) => (def_id
, true),
2467 _
=> return OtherUse(stmt
.source_info
.span
),
2471 "move_spans: def_id={:?} is_generator={:?} places={:?}",
2472 def_id
, is_generator
, places
2474 if let Some((args_span
, var_span
)) = self.closure_span(*def_id
, moved_place
, places
) {
2483 OtherUse(stmt
.source_info
.span
)
2486 /// Finds the span of arguments of a closure (within `maybe_closure_span`)
2487 /// and its usage of the local assigned at `location`.
2488 /// This is done by searching in statements succeeding `location`
2489 /// and originating from `maybe_closure_span`.
2490 pub(super) fn borrow_spans(&self, use_span
: Span
, location
: Location
) -> UseSpans
{
2491 use self::UseSpans
::*;
2492 debug
!("borrow_spans: use_span={:?} location={:?}", use_span
, location
);
2494 let target
= match self.mir
[location
.block
]
2496 .get(location
.statement_index
)
2499 kind
: StatementKind
::Assign(Place
::Local(local
), _
),
2502 _
=> return OtherUse(use_span
),
2505 if self.mir
.local_kind(target
) != LocalKind
::Temp
{
2506 // operands are always temporaries.
2507 return OtherUse(use_span
);
2510 for stmt
in &self.mir
[location
.block
].statements
[location
.statement_index
+ 1..] {
2511 if let StatementKind
::Assign(
2512 _
, box Rvalue
::Aggregate(ref kind
, ref places
)
2514 let (def_id
, is_generator
) = match kind
{
2515 box AggregateKind
::Closure(def_id
, _
) => (def_id
, false),
2516 box AggregateKind
::Generator(def_id
, _
, _
) => (def_id
, true),
2521 "borrow_spans: def_id={:?} is_generator={:?} places={:?}",
2522 def_id
, is_generator
, places
2524 if let Some((args_span
, var_span
)) = self.closure_span(
2525 *def_id
, &Place
::Local(target
), places
2533 return OtherUse(use_span
);
2537 if use_span
!= stmt
.source_info
.span
{
2545 /// Finds the span of a captured variable within a closure or generator.
2549 target_place
: &Place
<'tcx
>,
2550 places
: &Vec
<Operand
<'tcx
>>,
2551 ) -> Option
<(Span
, Span
)> {
2553 "closure_span: def_id={:?} target_place={:?} places={:?}",
2554 def_id
, target_place
, places
2556 let node_id
= self.infcx
.tcx
.hir().as_local_node_id(def_id
)?
;
2557 let expr
= &self.infcx
.tcx
.hir().expect_expr(node_id
).node
;
2558 debug
!("closure_span: node_id={:?} expr={:?}", node_id
, expr
);
2559 if let hir
::ExprKind
::Closure(
2562 let var_span
= self.infcx
.tcx
.with_freevars(
2565 for (v
, place
) in freevars
.iter().zip(places
) {
2567 Operand
::Copy(place
) |
2568 Operand
::Move(place
) if target_place
== place
=> {
2569 debug
!("closure_span: found captured local {:?}", place
);
2570 return Some(v
.span
);
2580 Some((*args_span
, var_span
))
2586 /// Helper to retrieve span(s) of given borrow from the current MIR
2588 pub(super) fn retrieve_borrow_spans(&self, borrow
: &BorrowData
<'_
>) -> UseSpans
{
2589 let span
= self.mir
.source_info(borrow
.reserve_location
).span
;
2590 self.borrow_spans(span
, borrow
.reserve_location
)