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