]> git.proxmox.com Git - rustc.git/blob - src/librustc_borrowck/borrowck/check_loans.rs
New upstream version 1.17.0+dfsg1
[rustc.git] / src / librustc_borrowck / borrowck / check_loans.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // ----------------------------------------------------------------------
12 // Checking loans
13 //
14 // Phase 2 of check: we walk down the tree and check that:
15 // 1. assignments are always made to mutable locations;
16 // 2. loans made in overlapping scopes do not conflict
17 // 3. assignments do not affect things loaned out as immutable
18 // 4. moves do not affect things loaned out in any way
19 use self::UseError::*;
20
21 use borrowck::*;
22 use borrowck::InteriorKind::{InteriorElement, InteriorField};
23 use rustc::middle::expr_use_visitor as euv;
24 use rustc::middle::expr_use_visitor::MutateMode;
25 use rustc::middle::mem_categorization as mc;
26 use rustc::middle::mem_categorization::Categorization;
27 use rustc::middle::region;
28 use rustc::ty::{self, TyCtxt};
29 use syntax::ast;
30 use syntax_pos::Span;
31 use rustc::hir;
32
33 use std::rc::Rc;
34
35 // FIXME (#16118): These functions are intended to allow the borrow checker to
36 // be less precise in its handling of Box while still allowing moves out of a
37 // Box. They should be removed when Unique is removed from LoanPath.
38
39 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
40 //! Returns the base of the leftmost dereference of an Unique in
41 //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
42 //! then it just returns `loan_path` itself.
43
44 return match helper(loan_path) {
45 Some(new_loan_path) => new_loan_path,
46 None => loan_path.clone()
47 };
48
49 fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
50 match loan_path.kind {
51 LpVar(_) | LpUpvar(_) => None,
52 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
53 match helper(&lp_base) {
54 v @ Some(_) => v,
55 None => Some(&lp_base)
56 }
57 }
58 LpDowncast(ref lp_base, _) |
59 LpExtend(ref lp_base, ..) => helper(&lp_base)
60 }
61 }
62 }
63
64 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
65 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
66 //! a &LoanPath.
67
68 return match helper(loan_path) {
69 Some(new_loan_path) => new_loan_path,
70 None => loan_path.clone()
71 };
72
73 fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
74 match loan_path.kind {
75 LpVar(_) | LpUpvar(_) => None,
76 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
77 match helper(lp_base) {
78 v @ Some(_) => v,
79 None => Some(lp_base.clone())
80 }
81 }
82 LpDowncast(ref lp_base, _) |
83 LpExtend(ref lp_base, ..) => helper(lp_base)
84 }
85 }
86 }
87
88 struct CheckLoanCtxt<'a, 'tcx: 'a> {
89 bccx: &'a BorrowckCtxt<'a, 'tcx>,
90 dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
91 move_data: &'a move_data::FlowedMoveData<'a, 'tcx>,
92 all_loans: &'a [Loan<'tcx>],
93 param_env: &'a ty::ParameterEnvironment<'tcx>,
94 }
95
96 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
97 fn consume(&mut self,
98 consume_id: ast::NodeId,
99 consume_span: Span,
100 cmt: mc::cmt<'tcx>,
101 mode: euv::ConsumeMode) {
102 debug!("consume(consume_id={}, cmt={:?}, mode={:?})",
103 consume_id, cmt, mode);
104
105 self.consume_common(consume_id, consume_span, cmt, mode);
106 }
107
108 fn matched_pat(&mut self,
109 _matched_pat: &hir::Pat,
110 _cmt: mc::cmt,
111 _mode: euv::MatchMode) { }
112
113 fn consume_pat(&mut self,
114 consume_pat: &hir::Pat,
115 cmt: mc::cmt<'tcx>,
116 mode: euv::ConsumeMode) {
117 debug!("consume_pat(consume_pat={:?}, cmt={:?}, mode={:?})",
118 consume_pat,
119 cmt,
120 mode);
121
122 self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
123 }
124
125 fn borrow(&mut self,
126 borrow_id: ast::NodeId,
127 borrow_span: Span,
128 cmt: mc::cmt<'tcx>,
129 loan_region: &'tcx ty::Region,
130 bk: ty::BorrowKind,
131 loan_cause: euv::LoanCause)
132 {
133 debug!("borrow(borrow_id={}, cmt={:?}, loan_region={:?}, \
134 bk={:?}, loan_cause={:?})",
135 borrow_id, cmt, loan_region,
136 bk, loan_cause);
137
138 if let Some(lp) = opt_loan_path(&cmt) {
139 let moved_value_use_kind = match loan_cause {
140 euv::ClosureCapture(_) => MovedInCapture,
141 _ => MovedInUse,
142 };
143 self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
144 }
145
146 self.check_for_conflicting_loans(borrow_id);
147 }
148
149 fn mutate(&mut self,
150 assignment_id: ast::NodeId,
151 assignment_span: Span,
152 assignee_cmt: mc::cmt<'tcx>,
153 mode: euv::MutateMode)
154 {
155 debug!("mutate(assignment_id={}, assignee_cmt={:?})",
156 assignment_id, assignee_cmt);
157
158 if let Some(lp) = opt_loan_path(&assignee_cmt) {
159 match mode {
160 MutateMode::Init | MutateMode::JustWrite => {
161 // In a case like `path = 1`, then path does not
162 // have to be *FULLY* initialized, but we still
163 // must be careful lest it contains derefs of
164 // pointers.
165 self.check_if_assigned_path_is_moved(assignee_cmt.id,
166 assignment_span,
167 MovedInUse,
168 &lp);
169 }
170 MutateMode::WriteAndRead => {
171 // In a case like `path += 1`, then path must be
172 // fully initialized, since we will read it before
173 // we write it.
174 self.check_if_path_is_moved(assignee_cmt.id,
175 assignment_span,
176 MovedInUse,
177 &lp);
178 }
179 }
180 }
181 self.check_assignment(assignment_id, assignment_span, assignee_cmt);
182 }
183
184 fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
185 }
186
187 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
188 dfcx_loans: &LoanDataFlow<'b, 'tcx>,
189 move_data: &move_data::FlowedMoveData<'c, 'tcx>,
190 all_loans: &[Loan<'tcx>],
191 body: &hir::Body) {
192 debug!("check_loans(body id={})", body.value.id);
193
194 let infcx = bccx.tcx.borrowck_fake_infer_ctxt(body.id());
195 let mut clcx = CheckLoanCtxt {
196 bccx: bccx,
197 dfcx_loans: dfcx_loans,
198 move_data: move_data,
199 all_loans: all_loans,
200 param_env: &infcx.parameter_environment
201 };
202 euv::ExprUseVisitor::new(&mut clcx, &infcx).consume_body(body);
203 }
204
205 #[derive(PartialEq)]
206 enum UseError<'tcx> {
207 UseOk,
208 UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
209 }
210
211 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
212 borrow_kind2: ty::BorrowKind)
213 -> bool {
214 borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
215 }
216
217 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
218 pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.bccx.tcx }
219
220 pub fn each_issued_loan<F>(&self, node: ast::NodeId, mut op: F) -> bool where
221 F: FnMut(&Loan<'tcx>) -> bool,
222 {
223 //! Iterates over each loan that has been issued
224 //! on entrance to `node`, regardless of whether it is
225 //! actually *in scope* at that point. Sometimes loans
226 //! are issued for future scopes and thus they may have been
227 //! *issued* but not yet be in effect.
228
229 self.dfcx_loans.each_bit_on_entry(node, |loan_index| {
230 let loan = &self.all_loans[loan_index];
231 op(loan)
232 })
233 }
234
235 pub fn each_in_scope_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
236 F: FnMut(&Loan<'tcx>) -> bool,
237 {
238 //! Like `each_issued_loan()`, but only considers loans that are
239 //! currently in scope.
240
241 let tcx = self.tcx();
242 self.each_issued_loan(scope.node_id(&tcx.region_maps), |loan| {
243 if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
244 op(loan)
245 } else {
246 true
247 }
248 })
249 }
250
251 fn each_in_scope_loan_affecting_path<F>(&self,
252 scope: region::CodeExtent,
253 loan_path: &LoanPath<'tcx>,
254 mut op: F)
255 -> bool where
256 F: FnMut(&Loan<'tcx>) -> bool,
257 {
258 //! Iterates through all of the in-scope loans affecting `loan_path`,
259 //! calling `op`, and ceasing iteration if `false` is returned.
260
261 // First, we check for a loan restricting the path P being used. This
262 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
263 // Consider the following example:
264 //
265 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
266 // let y = a; // Conflicts with restriction
267
268 let loan_path = owned_ptr_base_path(loan_path);
269 let cont = self.each_in_scope_loan(scope, |loan| {
270 let mut ret = true;
271 for restr_path in &loan.restricted_paths {
272 if **restr_path == *loan_path {
273 if !op(loan) {
274 ret = false;
275 break;
276 }
277 }
278 }
279 ret
280 });
281
282 if !cont {
283 return false;
284 }
285
286 // Next, we must check for *loans* (not restrictions) on the path P or
287 // any base path. This rejects examples like the following:
288 //
289 // let x = &mut a.b;
290 // let y = a.b.c;
291 //
292 // Limiting this search to *loans* and not *restrictions* means that
293 // examples like the following continue to work:
294 //
295 // let x = &mut a.b;
296 // let y = a.c;
297
298 let mut loan_path = loan_path;
299 loop {
300 match loan_path.kind {
301 LpVar(_) | LpUpvar(_) => {
302 break;
303 }
304 LpDowncast(ref lp_base, _) |
305 LpExtend(ref lp_base, ..) => {
306 loan_path = &lp_base;
307 }
308 }
309
310 let cont = self.each_in_scope_loan(scope, |loan| {
311 if *loan.loan_path == *loan_path {
312 op(loan)
313 } else {
314 true
315 }
316 });
317
318 if !cont {
319 return false;
320 }
321 }
322
323 return true;
324 }
325
326 pub fn loans_generated_by(&self, node: ast::NodeId) -> Vec<usize> {
327 //! Returns a vector of the loans that are generated as
328 //! we enter `node`.
329
330 let mut result = Vec::new();
331 self.dfcx_loans.each_gen_bit(node, |loan_index| {
332 result.push(loan_index);
333 true
334 });
335 return result;
336 }
337
338 pub fn check_for_conflicting_loans(&self, node: ast::NodeId) {
339 //! Checks to see whether any of the loans that are issued
340 //! on entrance to `node` conflict with loans that have already been
341 //! issued when we enter `node` (for example, we do not
342 //! permit two `&mut` borrows of the same variable).
343 //!
344 //! (Note that some loans can be *issued* without necessarily
345 //! taking effect yet.)
346
347 debug!("check_for_conflicting_loans(node={:?})", node);
348
349 let new_loan_indices = self.loans_generated_by(node);
350 debug!("new_loan_indices = {:?}", new_loan_indices);
351
352 for &new_loan_index in &new_loan_indices {
353 self.each_issued_loan(node, |issued_loan| {
354 let new_loan = &self.all_loans[new_loan_index];
355 // Only report an error for the first issued loan that conflicts
356 // to avoid O(n^2) errors.
357 self.report_error_if_loans_conflict(issued_loan, new_loan)
358 });
359 }
360
361 for (i, &x) in new_loan_indices.iter().enumerate() {
362 let old_loan = &self.all_loans[x];
363 for &y in &new_loan_indices[(i+1) ..] {
364 let new_loan = &self.all_loans[y];
365 self.report_error_if_loans_conflict(old_loan, new_loan);
366 }
367 }
368 }
369
370 pub fn report_error_if_loans_conflict(&self,
371 old_loan: &Loan<'tcx>,
372 new_loan: &Loan<'tcx>)
373 -> bool {
374 //! Checks whether `old_loan` and `new_loan` can safely be issued
375 //! simultaneously.
376
377 debug!("report_error_if_loans_conflict(old_loan={:?}, new_loan={:?})",
378 old_loan,
379 new_loan);
380
381 // Should only be called for loans that are in scope at the same time.
382 assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
383 new_loan.kill_scope));
384
385 self.report_error_if_loan_conflicts_with_restriction(
386 old_loan, new_loan, old_loan, new_loan) &&
387 self.report_error_if_loan_conflicts_with_restriction(
388 new_loan, old_loan, old_loan, new_loan)
389 }
390
391 pub fn report_error_if_loan_conflicts_with_restriction(&self,
392 loan1: &Loan<'tcx>,
393 loan2: &Loan<'tcx>,
394 old_loan: &Loan<'tcx>,
395 new_loan: &Loan<'tcx>)
396 -> bool {
397 //! Checks whether the restrictions introduced by `loan1` would
398 //! prohibit `loan2`. Returns false if an error is reported.
399
400 debug!("report_error_if_loan_conflicts_with_restriction(\
401 loan1={:?}, loan2={:?})",
402 loan1,
403 loan2);
404
405 if compatible_borrow_kinds(loan1.kind, loan2.kind) {
406 return true;
407 }
408
409 let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
410 for restr_path in &loan1.restricted_paths {
411 if *restr_path != loan2_base_path { continue; }
412
413 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
414 // normally generate a rather confusing message (in this case, for multiple mutable
415 // borrows):
416 //
417 // error: cannot borrow `x.b` as mutable more than once at a time
418 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
419 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
420 //
421 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
422 // use that for most of the message instead, giving is something like this:
423 //
424 // error: cannot borrow `x` as mutable more than once at a time
425 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
426 // borrow prevents subsequent moves, borrows, or modification of `x` until the
427 // borrow ends
428
429 let common = new_loan.loan_path.common(&old_loan.loan_path);
430 let (nl, ol, new_loan_msg, old_loan_msg) = {
431 if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() {
432 let nl = self.bccx.loan_path_to_string(&common.unwrap());
433 let ol = nl.clone();
434 let new_loan_msg = format!(" (via `{}`)",
435 self.bccx.loan_path_to_string(
436 &new_loan.loan_path));
437 let old_loan_msg = format!(" (via `{}`)",
438 self.bccx.loan_path_to_string(
439 &old_loan.loan_path));
440 (nl, ol, new_loan_msg, old_loan_msg)
441 } else {
442 (self.bccx.loan_path_to_string(&new_loan.loan_path),
443 self.bccx.loan_path_to_string(&old_loan.loan_path),
444 String::new(),
445 String::new())
446 }
447 };
448
449 let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
450 "it".to_string()
451 } else {
452 format!("`{}`", ol)
453 };
454
455 // We want to assemble all the relevant locations for the error.
456 //
457 // 1. Where did the new loan occur.
458 // - if due to closure creation, where was the variable used in closure?
459 // 2. Where did old loan occur.
460 // 3. Where does old loan expire.
461
462 let previous_end_span =
463 self.tcx().hir.span(old_loan.kill_scope.node_id(&self.tcx().region_maps))
464 .end_point();
465
466 let mut err = match (new_loan.kind, old_loan.kind) {
467 (ty::MutBorrow, ty::MutBorrow) => {
468 let mut err = struct_span_err!(self.bccx, new_loan.span, E0499,
469 "cannot borrow `{}`{} as mutable \
470 more than once at a time",
471 nl, new_loan_msg);
472 err.span_label(
473 old_loan.span,
474 &format!("first mutable borrow occurs here{}", old_loan_msg));
475 err.span_label(
476 new_loan.span,
477 &format!("second mutable borrow occurs here{}", new_loan_msg));
478 err.span_label(
479 previous_end_span,
480 &format!("first borrow ends here"));
481 err
482 }
483
484 (ty::UniqueImmBorrow, ty::UniqueImmBorrow) => {
485 let mut err = struct_span_err!(self.bccx, new_loan.span, E0524,
486 "two closures require unique access to `{}` \
487 at the same time",
488 nl);
489 err.span_label(
490 old_loan.span,
491 &format!("first closure is constructed here"));
492 err.span_label(
493 new_loan.span,
494 &format!("second closure is constructed here"));
495 err.span_label(
496 previous_end_span,
497 &format!("borrow from first closure ends here"));
498 err
499 }
500
501 (ty::UniqueImmBorrow, _) => {
502 let mut err = struct_span_err!(self.bccx, new_loan.span, E0500,
503 "closure requires unique access to `{}` \
504 but {} is already borrowed{}",
505 nl, ol_pronoun, old_loan_msg);
506 err.span_label(
507 new_loan.span,
508 &format!("closure construction occurs here{}", new_loan_msg));
509 err.span_label(
510 old_loan.span,
511 &format!("borrow occurs here{}", old_loan_msg));
512 err.span_label(
513 previous_end_span,
514 &format!("borrow ends here"));
515 err
516 }
517
518 (_, ty::UniqueImmBorrow) => {
519 let mut err = struct_span_err!(self.bccx, new_loan.span, E0501,
520 "cannot borrow `{}`{} as {} because \
521 previous closure requires unique access",
522 nl, new_loan_msg, new_loan.kind.to_user_str());
523 err.span_label(
524 new_loan.span,
525 &format!("borrow occurs here{}", new_loan_msg));
526 err.span_label(
527 old_loan.span,
528 &format!("closure construction occurs here{}", old_loan_msg));
529 err.span_label(
530 previous_end_span,
531 &format!("borrow from closure ends here"));
532 err
533 }
534
535 (..) => {
536 let mut err = struct_span_err!(self.bccx, new_loan.span, E0502,
537 "cannot borrow `{}`{} as {} because \
538 {} is also borrowed as {}{}",
539 nl,
540 new_loan_msg,
541 new_loan.kind.to_user_str(),
542 ol_pronoun,
543 old_loan.kind.to_user_str(),
544 old_loan_msg);
545 err.span_label(
546 new_loan.span,
547 &format!("{} borrow occurs here{}",
548 new_loan.kind.to_user_str(),
549 new_loan_msg));
550 err.span_label(
551 old_loan.span,
552 &format!("{} borrow occurs here{}",
553 old_loan.kind.to_user_str(),
554 old_loan_msg));
555 err.span_label(
556 previous_end_span,
557 &format!("{} borrow ends here",
558 old_loan.kind.to_user_str()));
559 err
560 }
561 };
562
563 match new_loan.cause {
564 euv::ClosureCapture(span) => {
565 err.span_label(
566 span,
567 &format!("borrow occurs due to use of `{}` in closure", nl));
568 }
569 _ => { }
570 }
571
572 match old_loan.cause {
573 euv::ClosureCapture(span) => {
574 err.span_label(
575 span,
576 &format!("previous borrow occurs due to use of `{}` in closure",
577 ol));
578 }
579 _ => { }
580 }
581
582 err.emit();
583 return false;
584 }
585
586 true
587 }
588
589 fn consume_common(&self,
590 id: ast::NodeId,
591 span: Span,
592 cmt: mc::cmt<'tcx>,
593 mode: euv::ConsumeMode) {
594 if let Some(lp) = opt_loan_path(&cmt) {
595 let moved_value_use_kind = match mode {
596 euv::Copy => {
597 self.check_for_copy_of_frozen_path(id, span, &lp);
598 MovedInUse
599 }
600 euv::Move(_) => {
601 match self.move_data.kind_of_move_of_path(id, &lp) {
602 None => {
603 // Sometimes moves don't have a move kind;
604 // this either means that the original move
605 // was from something illegal to move,
606 // or was moved from referent of an unsafe
607 // pointer or something like that.
608 MovedInUse
609 }
610 Some(move_kind) => {
611 self.check_for_move_of_borrowed_path(id, span,
612 &lp, move_kind);
613 if move_kind == move_data::Captured {
614 MovedInCapture
615 } else {
616 MovedInUse
617 }
618 }
619 }
620 }
621 };
622
623 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
624 }
625 }
626
627 fn check_for_copy_of_frozen_path(&self,
628 id: ast::NodeId,
629 span: Span,
630 copy_path: &LoanPath<'tcx>) {
631 match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
632 UseOk => { }
633 UseWhileBorrowed(loan_path, loan_span) => {
634 struct_span_err!(self.bccx, span, E0503,
635 "cannot use `{}` because it was mutably borrowed",
636 &self.bccx.loan_path_to_string(copy_path))
637 .span_label(loan_span,
638 &format!("borrow of `{}` occurs here",
639 &self.bccx.loan_path_to_string(&loan_path))
640 )
641 .span_label(span,
642 &format!("use of borrowed `{}`",
643 &self.bccx.loan_path_to_string(&loan_path)))
644 .emit();
645 }
646 }
647 }
648
649 fn check_for_move_of_borrowed_path(&self,
650 id: ast::NodeId,
651 span: Span,
652 move_path: &LoanPath<'tcx>,
653 move_kind: move_data::MoveKind) {
654 // We want to detect if there are any loans at all, so we search for
655 // any loans incompatible with MutBorrrow, since all other kinds of
656 // loans are incompatible with that.
657 match self.analyze_restrictions_on_use(id, move_path, ty::MutBorrow) {
658 UseOk => { }
659 UseWhileBorrowed(loan_path, loan_span) => {
660 let mut err = match move_kind {
661 move_data::Captured => {
662 let mut err = struct_span_err!(self.bccx, span, E0504,
663 "cannot move `{}` into closure because it is borrowed",
664 &self.bccx.loan_path_to_string(move_path));
665 err.span_label(
666 loan_span,
667 &format!("borrow of `{}` occurs here",
668 &self.bccx.loan_path_to_string(&loan_path))
669 );
670 err.span_label(
671 span,
672 &format!("move into closure occurs here")
673 );
674 err
675 }
676 move_data::Declared |
677 move_data::MoveExpr |
678 move_data::MovePat => {
679 let mut err = struct_span_err!(self.bccx, span, E0505,
680 "cannot move out of `{}` because it is borrowed",
681 &self.bccx.loan_path_to_string(move_path));
682 err.span_label(
683 loan_span,
684 &format!("borrow of `{}` occurs here",
685 &self.bccx.loan_path_to_string(&loan_path))
686 );
687 err.span_label(
688 span,
689 &format!("move out of `{}` occurs here",
690 &self.bccx.loan_path_to_string(move_path))
691 );
692 err
693 }
694 };
695
696 err.emit();
697 }
698 }
699 }
700
701 pub fn analyze_restrictions_on_use(&self,
702 expr_id: ast::NodeId,
703 use_path: &LoanPath<'tcx>,
704 borrow_kind: ty::BorrowKind)
705 -> UseError<'tcx> {
706 debug!("analyze_restrictions_on_use(expr_id={}, use_path={:?})",
707 self.tcx().hir.node_to_string(expr_id),
708 use_path);
709
710 let mut ret = UseOk;
711
712 self.each_in_scope_loan_affecting_path(
713 self.tcx().region_maps.node_extent(expr_id), use_path, |loan| {
714 if !compatible_borrow_kinds(loan.kind, borrow_kind) {
715 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
716 false
717 } else {
718 true
719 }
720 });
721
722 return ret;
723 }
724
725 /// Reports an error if `expr` (which should be a path)
726 /// is using a moved/uninitialized value
727 fn check_if_path_is_moved(&self,
728 id: ast::NodeId,
729 span: Span,
730 use_kind: MovedValueUseKind,
731 lp: &Rc<LoanPath<'tcx>>) {
732 debug!("check_if_path_is_moved(id={}, use_kind={:?}, lp={:?})",
733 id, use_kind, lp);
734
735 // FIXME (22079): if you find yourself tempted to cut and paste
736 // the body below and then specializing the error reporting,
737 // consider refactoring this instead!
738
739 let base_lp = owned_ptr_base_path_rc(lp);
740 self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
741 self.bccx.report_use_of_moved_value(
742 span,
743 use_kind,
744 &lp,
745 the_move,
746 moved_lp,
747 self.param_env);
748 false
749 });
750 }
751
752 /// Reports an error if assigning to `lp` will use a
753 /// moved/uninitialized value. Mainly this is concerned with
754 /// detecting derefs of uninitialized pointers.
755 ///
756 /// For example:
757 ///
758 /// ```ignore
759 /// let a: i32;
760 /// a = 10; // ok, even though a is uninitialized
761 ///
762 /// struct Point { x: u32, y: u32 }
763 /// let p: Point;
764 /// p.x = 22; // ok, even though `p` is uninitialized
765 ///
766 /// let p: Box<Point>;
767 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
768 /// ```
769 fn check_if_assigned_path_is_moved(&self,
770 id: ast::NodeId,
771 span: Span,
772 use_kind: MovedValueUseKind,
773 lp: &Rc<LoanPath<'tcx>>)
774 {
775 match lp.kind {
776 LpVar(_) | LpUpvar(_) => {
777 // assigning to `x` does not require that `x` is initialized
778 }
779 LpDowncast(ref lp_base, _) => {
780 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
781 self.check_if_assigned_path_is_moved(id, span,
782 use_kind, lp_base);
783 }
784 LpExtend(ref lp_base, _, LpInterior(_, InteriorField(_))) => {
785 match lp_base.to_type().sty {
786 ty::TyAdt(def, _) if def.has_dtor(self.tcx()) => {
787 // In the case where the owner implements drop, then
788 // the path must be initialized to prevent a case of
789 // partial reinitialization
790 //
791 // FIXME (22079): could refactor via hypothetical
792 // generalized check_if_path_is_moved
793 let loan_path = owned_ptr_base_path_rc(lp_base);
794 self.move_data.each_move_of(id, &loan_path, |_, _| {
795 self.bccx
796 .report_partial_reinitialization_of_uninitialized_structure(
797 span,
798 &loan_path);
799 false
800 });
801 return;
802 },
803 _ => {},
804 }
805
806 // assigning to `P.f` is ok if assigning to `P` is ok
807 self.check_if_assigned_path_is_moved(id, span,
808 use_kind, lp_base);
809 }
810 LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) |
811 LpExtend(ref lp_base, _, LpDeref(_)) => {
812 // assigning to `P[i]` requires `P` is initialized
813 // assigning to `(*P)` requires `P` is initialized
814 self.check_if_path_is_moved(id, span, use_kind, lp_base);
815 }
816 }
817 }
818
819 fn check_assignment(&self,
820 assignment_id: ast::NodeId,
821 assignment_span: Span,
822 assignee_cmt: mc::cmt<'tcx>) {
823 debug!("check_assignment(assignee_cmt={:?})", assignee_cmt);
824
825 // Check that we don't invalidate any outstanding loans
826 if let Some(loan_path) = opt_loan_path(&assignee_cmt) {
827 let scope = self.tcx().region_maps.node_extent(assignment_id);
828 self.each_in_scope_loan_affecting_path(scope, &loan_path, |loan| {
829 self.report_illegal_mutation(assignment_span, &loan_path, loan);
830 false
831 });
832 }
833
834 // Check for reassignments to (immutable) local variables. This
835 // needs to be done here instead of in check_loans because we
836 // depend on move data.
837 if let Categorization::Local(local_id) = assignee_cmt.cat {
838 let lp = opt_loan_path(&assignee_cmt).unwrap();
839 self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
840 if assignee_cmt.mutbl.is_mutable() {
841 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
842 } else {
843 self.bccx.report_reassigned_immutable_variable(
844 assignment_span,
845 &lp,
846 assign);
847 }
848 false
849 });
850 return
851 }
852 }
853
854 pub fn report_illegal_mutation(&self,
855 span: Span,
856 loan_path: &LoanPath<'tcx>,
857 loan: &Loan) {
858 struct_span_err!(self.bccx, span, E0506,
859 "cannot assign to `{}` because it is borrowed",
860 self.bccx.loan_path_to_string(loan_path))
861 .span_label(loan.span,
862 &format!("borrow of `{}` occurs here",
863 self.bccx.loan_path_to_string(loan_path)))
864 .span_label(span,
865 &format!("assignment to borrowed `{}` occurs here",
866 self.bccx.loan_path_to_string(loan_path)))
867 .emit();
868 }
869 }