]> git.proxmox.com Git - rustc.git/blob - src/librustc_borrowck/borrowck/check_loans.rs
Merge tag 'upstream-tar/1.0.0_0alpha'
[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 rustc::middle::expr_use_visitor as euv;
23 use rustc::middle::mem_categorization as mc;
24 use rustc::middle::region;
25 use rustc::middle::ty;
26 use rustc::util::ppaux::Repr;
27 use syntax::ast;
28 use syntax::codemap::Span;
29
30 use std::rc::Rc;
31
32 // FIXME (#16118): These functions are intended to allow the borrow checker to
33 // be less precise in its handling of Box while still allowing moves out of a
34 // Box. They should be removed when Unique is removed from LoanPath.
35
36 fn owned_ptr_base_path<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> &'a LoanPath<'tcx> {
37 //! Returns the base of the leftmost dereference of an Unique in
38 //! `loan_path`. If there is no dereference of an Unique in `loan_path`,
39 //! then it just returns `loan_path` itself.
40
41 return match helper(loan_path) {
42 Some(new_loan_path) => new_loan_path,
43 None => loan_path.clone()
44 };
45
46 fn helper<'a, 'tcx>(loan_path: &'a LoanPath<'tcx>) -> Option<&'a LoanPath<'tcx>> {
47 match loan_path.kind {
48 LpVar(_) | LpUpvar(_) => None,
49 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
50 match helper(&**lp_base) {
51 v @ Some(_) => v,
52 None => Some(&**lp_base)
53 }
54 }
55 LpDowncast(ref lp_base, _) |
56 LpExtend(ref lp_base, _, _) => helper(&**lp_base)
57 }
58 }
59 }
60
61 fn owned_ptr_base_path_rc<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Rc<LoanPath<'tcx>> {
62 //! The equivalent of `owned_ptr_base_path` for an &Rc<LoanPath> rather than
63 //! a &LoanPath.
64
65 return match helper(loan_path) {
66 Some(new_loan_path) => new_loan_path,
67 None => loan_path.clone()
68 };
69
70 fn helper<'tcx>(loan_path: &Rc<LoanPath<'tcx>>) -> Option<Rc<LoanPath<'tcx>>> {
71 match loan_path.kind {
72 LpVar(_) | LpUpvar(_) => None,
73 LpExtend(ref lp_base, _, LpDeref(mc::Unique)) => {
74 match helper(lp_base) {
75 v @ Some(_) => v,
76 None => Some(lp_base.clone())
77 }
78 }
79 LpDowncast(ref lp_base, _) |
80 LpExtend(ref lp_base, _, _) => helper(lp_base)
81 }
82 }
83 }
84
85 struct CheckLoanCtxt<'a, 'tcx: 'a> {
86 bccx: &'a BorrowckCtxt<'a, 'tcx>,
87 dfcx_loans: &'a LoanDataFlow<'a, 'tcx>,
88 move_data: move_data::FlowedMoveData<'a, 'tcx>,
89 all_loans: &'a [Loan<'tcx>],
90 param_env: &'a ty::ParameterEnvironment<'a, 'tcx>,
91 }
92
93 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckLoanCtxt<'a, 'tcx> {
94 fn consume(&mut self,
95 consume_id: ast::NodeId,
96 consume_span: Span,
97 cmt: mc::cmt<'tcx>,
98 mode: euv::ConsumeMode) {
99 debug!("consume(consume_id={}, cmt={}, mode={:?})",
100 consume_id, cmt.repr(self.tcx()), mode);
101
102 self.consume_common(consume_id, consume_span, cmt, mode);
103 }
104
105 fn matched_pat(&mut self,
106 _matched_pat: &ast::Pat,
107 _cmt: mc::cmt,
108 _mode: euv::MatchMode) { }
109
110 fn consume_pat(&mut self,
111 consume_pat: &ast::Pat,
112 cmt: mc::cmt<'tcx>,
113 mode: euv::ConsumeMode) {
114 debug!("consume_pat(consume_pat={}, cmt={}, mode={:?})",
115 consume_pat.repr(self.tcx()),
116 cmt.repr(self.tcx()),
117 mode);
118
119 self.consume_common(consume_pat.id, consume_pat.span, cmt, mode);
120 }
121
122 fn borrow(&mut self,
123 borrow_id: ast::NodeId,
124 borrow_span: Span,
125 cmt: mc::cmt<'tcx>,
126 loan_region: ty::Region,
127 bk: ty::BorrowKind,
128 loan_cause: euv::LoanCause)
129 {
130 debug!("borrow(borrow_id={}, cmt={}, loan_region={:?}, \
131 bk={:?}, loan_cause={:?})",
132 borrow_id, cmt.repr(self.tcx()), loan_region,
133 bk, loan_cause);
134
135 match opt_loan_path(&cmt) {
136 Some(lp) => {
137 let moved_value_use_kind = match loan_cause {
138 euv::ClosureCapture(_) => MovedInCapture,
139 _ => MovedInUse,
140 };
141 self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp);
142 }
143 None => { }
144 }
145
146 self.check_for_conflicting_loans(region::CodeExtent::from_node_id(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.repr(self.tcx()));
157
158 match opt_loan_path(&assignee_cmt) {
159 Some(lp) => {
160 match mode {
161 euv::Init | euv::JustWrite => {
162 // In a case like `path = 1`, then path does not
163 // have to be *FULLY* initialized, but we still
164 // must be careful lest it contains derefs of
165 // pointers.
166 self.check_if_assigned_path_is_moved(assignee_cmt.id,
167 assignment_span,
168 MovedInUse,
169 &lp);
170 }
171 euv::WriteAndRead => {
172 // In a case like `path += 1`, then path must be
173 // fully initialized, since we will read it before
174 // we write it.
175 self.check_if_path_is_moved(assignee_cmt.id,
176 assignment_span,
177 MovedInUse,
178 &lp);
179 }
180 }
181 }
182 None => { }
183 }
184
185 self.check_assignment(assignment_id, assignment_span, assignee_cmt, mode);
186 }
187
188 fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { }
189 }
190
191 pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
192 dfcx_loans: &LoanDataFlow<'b, 'tcx>,
193 move_data: move_data::FlowedMoveData<'c, 'tcx>,
194 all_loans: &[Loan<'tcx>],
195 fn_id: ast::NodeId,
196 decl: &ast::FnDecl,
197 body: &ast::Block) {
198 debug!("check_loans(body id={})", body.id);
199
200 let param_env = ty::ParameterEnvironment::for_item(bccx.tcx, fn_id);
201
202 let mut clcx = CheckLoanCtxt {
203 bccx: bccx,
204 dfcx_loans: dfcx_loans,
205 move_data: move_data,
206 all_loans: all_loans,
207 param_env: &param_env,
208 };
209
210 {
211 let mut euv = euv::ExprUseVisitor::new(&mut clcx, &param_env);
212 euv.walk_fn(decl, body);
213 }
214 }
215
216 #[derive(PartialEq)]
217 enum UseError<'tcx> {
218 UseOk,
219 UseWhileBorrowed(/*loan*/Rc<LoanPath<'tcx>>, /*loan*/Span)
220 }
221
222 fn compatible_borrow_kinds(borrow_kind1: ty::BorrowKind,
223 borrow_kind2: ty::BorrowKind)
224 -> bool {
225 borrow_kind1 == ty::ImmBorrow && borrow_kind2 == ty::ImmBorrow
226 }
227
228 impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
229 pub fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.bccx.tcx }
230
231 pub fn each_issued_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
232 F: FnMut(&Loan<'tcx>) -> bool,
233 {
234 //! Iterates over each loan that has been issued
235 //! on entrance to `scope`, regardless of whether it is
236 //! actually *in scope* at that point. Sometimes loans
237 //! are issued for future scopes and thus they may have been
238 //! *issued* but not yet be in effect.
239
240 self.dfcx_loans.each_bit_on_entry(scope.node_id(), |loan_index| {
241 let loan = &self.all_loans[loan_index];
242 op(loan)
243 })
244 }
245
246 pub fn each_in_scope_loan<F>(&self, scope: region::CodeExtent, mut op: F) -> bool where
247 F: FnMut(&Loan<'tcx>) -> bool,
248 {
249 //! Like `each_issued_loan()`, but only considers loans that are
250 //! currently in scope.
251
252 let tcx = self.tcx();
253 self.each_issued_loan(scope, |loan| {
254 if tcx.region_maps.is_subscope_of(scope, loan.kill_scope) {
255 op(loan)
256 } else {
257 true
258 }
259 })
260 }
261
262 fn each_in_scope_loan_affecting_path<F>(&self,
263 scope: region::CodeExtent,
264 loan_path: &LoanPath<'tcx>,
265 mut op: F)
266 -> bool where
267 F: FnMut(&Loan<'tcx>) -> bool,
268 {
269 //! Iterates through all of the in-scope loans affecting `loan_path`,
270 //! calling `op`, and ceasing iteration if `false` is returned.
271
272 // First, we check for a loan restricting the path P being used. This
273 // accounts for borrows of P but also borrows of subpaths, like P.a.b.
274 // Consider the following example:
275 //
276 // let x = &mut a.b.c; // Restricts a, a.b, and a.b.c
277 // let y = a; // Conflicts with restriction
278
279 let loan_path = owned_ptr_base_path(loan_path);
280 let cont = self.each_in_scope_loan(scope, |loan| {
281 let mut ret = true;
282 for restr_path in loan.restricted_paths.iter() {
283 if **restr_path == *loan_path {
284 if !op(loan) {
285 ret = false;
286 break;
287 }
288 }
289 }
290 ret
291 });
292
293 if !cont {
294 return false;
295 }
296
297 // Next, we must check for *loans* (not restrictions) on the path P or
298 // any base path. This rejects examples like the following:
299 //
300 // let x = &mut a.b;
301 // let y = a.b.c;
302 //
303 // Limiting this search to *loans* and not *restrictions* means that
304 // examples like the following continue to work:
305 //
306 // let x = &mut a.b;
307 // let y = a.c;
308
309 let mut loan_path = loan_path;
310 loop {
311 match loan_path.kind {
312 LpVar(_) | LpUpvar(_) => {
313 break;
314 }
315 LpDowncast(ref lp_base, _) |
316 LpExtend(ref lp_base, _, _) => {
317 loan_path = &**lp_base;
318 }
319 }
320
321 let cont = self.each_in_scope_loan(scope, |loan| {
322 if *loan.loan_path == *loan_path {
323 op(loan)
324 } else {
325 true
326 }
327 });
328
329 if !cont {
330 return false;
331 }
332 }
333
334 return true;
335 }
336
337 pub fn loans_generated_by(&self, scope: region::CodeExtent) -> Vec<uint> {
338 //! Returns a vector of the loans that are generated as
339 //! we enter `scope`.
340
341 let mut result = Vec::new();
342 self.dfcx_loans.each_gen_bit(scope.node_id(), |loan_index| {
343 result.push(loan_index);
344 true
345 });
346 return result;
347 }
348
349 pub fn check_for_conflicting_loans(&self, scope: region::CodeExtent) {
350 //! Checks to see whether any of the loans that are issued
351 //! on entrance to `scope` conflict with loans that have already been
352 //! issued when we enter `scope` (for example, we do not
353 //! permit two `&mut` borrows of the same variable).
354 //!
355 //! (Note that some loans can be *issued* without necessarily
356 //! taking effect yet.)
357
358 debug!("check_for_conflicting_loans(scope={:?})", scope);
359
360 let new_loan_indices = self.loans_generated_by(scope);
361 debug!("new_loan_indices = {:?}", new_loan_indices);
362
363 self.each_issued_loan(scope, |issued_loan| {
364 for &new_loan_index in new_loan_indices.iter() {
365 let new_loan = &self.all_loans[new_loan_index];
366 self.report_error_if_loans_conflict(issued_loan, new_loan);
367 }
368 true
369 });
370
371 for (i, &x) in new_loan_indices.iter().enumerate() {
372 let old_loan = &self.all_loans[x];
373 for &y in new_loan_indices.slice_from(i+1).iter() {
374 let new_loan = &self.all_loans[y];
375 self.report_error_if_loans_conflict(old_loan, new_loan);
376 }
377 }
378 }
379
380 pub fn report_error_if_loans_conflict(&self,
381 old_loan: &Loan<'tcx>,
382 new_loan: &Loan<'tcx>) {
383 //! Checks whether `old_loan` and `new_loan` can safely be issued
384 //! simultaneously.
385
386 debug!("report_error_if_loans_conflict(old_loan={}, new_loan={})",
387 old_loan.repr(self.tcx()),
388 new_loan.repr(self.tcx()));
389
390 // Should only be called for loans that are in scope at the same time.
391 assert!(self.tcx().region_maps.scopes_intersect(old_loan.kill_scope,
392 new_loan.kill_scope));
393
394 self.report_error_if_loan_conflicts_with_restriction(
395 old_loan, new_loan, old_loan, new_loan) &&
396 self.report_error_if_loan_conflicts_with_restriction(
397 new_loan, old_loan, old_loan, new_loan);
398 }
399
400 pub fn report_error_if_loan_conflicts_with_restriction(&self,
401 loan1: &Loan<'tcx>,
402 loan2: &Loan<'tcx>,
403 old_loan: &Loan<'tcx>,
404 new_loan: &Loan<'tcx>)
405 -> bool {
406 //! Checks whether the restrictions introduced by `loan1` would
407 //! prohibit `loan2`. Returns false if an error is reported.
408
409 debug!("report_error_if_loan_conflicts_with_restriction(\
410 loan1={}, loan2={})",
411 loan1.repr(self.tcx()),
412 loan2.repr(self.tcx()));
413
414 if compatible_borrow_kinds(loan1.kind, loan2.kind) {
415 return true;
416 }
417
418 let loan2_base_path = owned_ptr_base_path_rc(&loan2.loan_path);
419 for restr_path in loan1.restricted_paths.iter() {
420 if *restr_path != loan2_base_path { continue; }
421
422 // If new_loan is something like `x.a`, and old_loan is something like `x.b`, we would
423 // normally generate a rather confusing message (in this case, for multiple mutable
424 // borrows):
425 //
426 // error: cannot borrow `x.b` as mutable more than once at a time
427 // note: previous borrow of `x.a` occurs here; the mutable borrow prevents
428 // subsequent moves, borrows, or modification of `x.a` until the borrow ends
429 //
430 // What we want to do instead is get the 'common ancestor' of the two borrow paths and
431 // use that for most of the message instead, giving is something like this:
432 //
433 // error: cannot borrow `x` as mutable more than once at a time
434 // note: previous borrow of `x` occurs here (through borrowing `x.a`); the mutable
435 // borrow prevents subsequent moves, borrows, or modification of `x` until the
436 // borrow ends
437
438 let common = new_loan.loan_path.common(&*old_loan.loan_path);
439 let (nl, ol, new_loan_msg, old_loan_msg) =
440 if new_loan.loan_path.has_fork(&*old_loan.loan_path) && common.is_some() {
441 let nl = self.bccx.loan_path_to_string(&common.unwrap());
442 let ol = nl.clone();
443 let new_loan_msg = format!(" (here through borrowing `{}`)",
444 self.bccx.loan_path_to_string(
445 &*new_loan.loan_path));
446 let old_loan_msg = format!(" (through borrowing `{}`)",
447 self.bccx.loan_path_to_string(
448 &*old_loan.loan_path));
449 (nl, ol, new_loan_msg, old_loan_msg)
450 } else {
451 (self.bccx.loan_path_to_string(&*new_loan.loan_path),
452 self.bccx.loan_path_to_string(&*old_loan.loan_path),
453 String::new(), String::new())
454 };
455
456 let ol_pronoun = if new_loan.loan_path == old_loan.loan_path {
457 "it".to_string()
458 } else {
459 format!("`{}`", ol)
460 };
461
462 match (new_loan.kind, old_loan.kind) {
463 (ty::MutBorrow, ty::MutBorrow) => {
464 self.bccx.span_err(
465 new_loan.span,
466 &format!("cannot borrow `{}`{} as mutable \
467 more than once at a time",
468 nl, new_loan_msg)[])
469 }
470
471 (ty::UniqueImmBorrow, _) => {
472 self.bccx.span_err(
473 new_loan.span,
474 &format!("closure requires unique access to `{}` \
475 but {} is already borrowed{}",
476 nl, ol_pronoun, old_loan_msg)[]);
477 }
478
479 (_, ty::UniqueImmBorrow) => {
480 self.bccx.span_err(
481 new_loan.span,
482 &format!("cannot borrow `{}`{} as {} because \
483 previous closure requires unique access",
484 nl, new_loan_msg, new_loan.kind.to_user_str())[]);
485 }
486
487 (_, _) => {
488 self.bccx.span_err(
489 new_loan.span,
490 &format!("cannot borrow `{}`{} as {} because \
491 {} is also borrowed as {}{}",
492 nl,
493 new_loan_msg,
494 new_loan.kind.to_user_str(),
495 ol_pronoun,
496 old_loan.kind.to_user_str(),
497 old_loan_msg)[]);
498 }
499 }
500
501 match new_loan.cause {
502 euv::ClosureCapture(span) => {
503 self.bccx.span_note(
504 span,
505 &format!("borrow occurs due to use of `{}` in closure",
506 nl)[]);
507 }
508 _ => { }
509 }
510
511 let rule_summary = match old_loan.kind {
512 ty::MutBorrow => {
513 format!("the mutable borrow prevents subsequent \
514 moves, borrows, or modification of `{0}` \
515 until the borrow ends",
516 ol)
517 }
518
519 ty::ImmBorrow => {
520 format!("the immutable borrow prevents subsequent \
521 moves or mutable borrows of `{0}` \
522 until the borrow ends",
523 ol)
524 }
525
526 ty::UniqueImmBorrow => {
527 format!("the unique capture prevents subsequent \
528 moves or borrows of `{0}` \
529 until the borrow ends",
530 ol)
531 }
532 };
533
534 let borrow_summary = match old_loan.cause {
535 euv::ClosureCapture(_) => {
536 format!("previous borrow of `{}` occurs here{} due to \
537 use in closure",
538 ol, old_loan_msg)
539 }
540
541 euv::OverloadedOperator(..) |
542 euv::AddrOf(..) |
543 euv::AutoRef(..) |
544 euv::ClosureInvocation(..) |
545 euv::ForLoop(..) |
546 euv::RefBinding(..) |
547 euv::MatchDiscriminant(..) => {
548 format!("previous borrow of `{}` occurs here{}",
549 ol, old_loan_msg)
550 }
551 };
552
553 self.bccx.span_note(
554 old_loan.span,
555 &format!("{}; {}", borrow_summary, rule_summary)[]);
556
557 let old_loan_span = self.tcx().map.span(old_loan.kill_scope.node_id());
558 self.bccx.span_end_note(old_loan_span,
559 "previous borrow ends here");
560
561 return false;
562 }
563
564 true
565 }
566
567 fn is_local_variable_or_arg(&self, cmt: mc::cmt<'tcx>) -> bool {
568 match cmt.cat {
569 mc::cat_local(_) => true,
570 _ => false
571 }
572 }
573
574 fn consume_common(&self,
575 id: ast::NodeId,
576 span: Span,
577 cmt: mc::cmt<'tcx>,
578 mode: euv::ConsumeMode) {
579 match opt_loan_path(&cmt) {
580 Some(lp) => {
581 let moved_value_use_kind = match mode {
582 euv::Copy => {
583 self.check_for_copy_of_frozen_path(id, span, &*lp);
584 MovedInUse
585 }
586 euv::Move(_) => {
587 match self.move_data.kind_of_move_of_path(id, &lp) {
588 None => {
589 // Sometimes moves don't have a move kind;
590 // this either means that the original move
591 // was from something illegal to move,
592 // or was moved from referent of an unsafe
593 // pointer or something like that.
594 MovedInUse
595 }
596 Some(move_kind) => {
597 self.check_for_move_of_borrowed_path(id, span,
598 &*lp, move_kind);
599 if move_kind == move_data::Captured {
600 MovedInCapture
601 } else {
602 MovedInUse
603 }
604 }
605 }
606 }
607 };
608
609 self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp);
610 }
611 None => { }
612 }
613 }
614
615 fn check_for_copy_of_frozen_path(&self,
616 id: ast::NodeId,
617 span: Span,
618 copy_path: &LoanPath<'tcx>) {
619 match self.analyze_restrictions_on_use(id, copy_path, ty::ImmBorrow) {
620 UseOk => { }
621 UseWhileBorrowed(loan_path, loan_span) => {
622 self.bccx.span_err(
623 span,
624 &format!("cannot use `{}` because it was mutably borrowed",
625 &self.bccx.loan_path_to_string(copy_path)[])
626 []);
627 self.bccx.span_note(
628 loan_span,
629 &format!("borrow of `{}` occurs here",
630 &self.bccx.loan_path_to_string(&*loan_path)[])
631 []);
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 err_message = match move_kind {
648 move_data::Captured =>
649 format!("cannot move `{}` into closure because it is borrowed",
650 &self.bccx.loan_path_to_string(move_path)[]),
651 move_data::Declared |
652 move_data::MoveExpr |
653 move_data::MovePat =>
654 format!("cannot move out of `{}` because it is borrowed",
655 &self.bccx.loan_path_to_string(move_path)[])
656 };
657
658 self.bccx.span_err(span, &err_message[]);
659 self.bccx.span_note(
660 loan_span,
661 &format!("borrow of `{}` occurs here",
662 &self.bccx.loan_path_to_string(&*loan_path)[])
663 []);
664 }
665 }
666 }
667
668 pub fn analyze_restrictions_on_use(&self,
669 expr_id: ast::NodeId,
670 use_path: &LoanPath<'tcx>,
671 borrow_kind: ty::BorrowKind)
672 -> UseError<'tcx> {
673 debug!("analyze_restrictions_on_use(expr_id={}, use_path={})",
674 self.tcx().map.node_to_string(expr_id),
675 use_path.repr(self.tcx()));
676
677 let mut ret = UseOk;
678
679 self.each_in_scope_loan_affecting_path(
680 region::CodeExtent::from_node_id(expr_id), use_path, |loan| {
681 if !compatible_borrow_kinds(loan.kind, borrow_kind) {
682 ret = UseWhileBorrowed(loan.loan_path.clone(), loan.span);
683 false
684 } else {
685 true
686 }
687 });
688
689 return ret;
690 }
691
692 /// Reports an error if `expr` (which should be a path)
693 /// is using a moved/uninitialized value
694 fn check_if_path_is_moved(&self,
695 id: ast::NodeId,
696 span: Span,
697 use_kind: MovedValueUseKind,
698 lp: &Rc<LoanPath<'tcx>>) {
699 debug!("check_if_path_is_moved(id={}, use_kind={:?}, lp={})",
700 id, use_kind, lp.repr(self.bccx.tcx));
701 let base_lp = owned_ptr_base_path_rc(lp);
702 self.move_data.each_move_of(id, &base_lp, |the_move, moved_lp| {
703 self.bccx.report_use_of_moved_value(
704 span,
705 use_kind,
706 &**lp,
707 the_move,
708 moved_lp,
709 self.param_env);
710 false
711 });
712 }
713
714 /// Reports an error if assigning to `lp` will use a
715 /// moved/uninitialized value. Mainly this is concerned with
716 /// detecting derefs of uninitialized pointers.
717 ///
718 /// For example:
719 ///
720 /// ```
721 /// let a: int;
722 /// a = 10; // ok, even though a is uninitialized
723 ///
724 /// struct Point { x: uint, y: uint }
725 /// let p: Point;
726 /// p.x = 22; // ok, even though `p` is uninitialized
727 ///
728 /// let p: ~Point;
729 /// (*p).x = 22; // not ok, p is uninitialized, can't deref
730 /// ```
731 fn check_if_assigned_path_is_moved(&self,
732 id: ast::NodeId,
733 span: Span,
734 use_kind: MovedValueUseKind,
735 lp: &Rc<LoanPath<'tcx>>)
736 {
737 match lp.kind {
738 LpVar(_) | LpUpvar(_) => {
739 // assigning to `x` does not require that `x` is initialized
740 }
741 LpDowncast(ref lp_base, _) => {
742 // assigning to `(P->Variant).f` is ok if assigning to `P` is ok
743 self.check_if_assigned_path_is_moved(id, span,
744 use_kind, lp_base);
745 }
746 LpExtend(ref lp_base, _, LpInterior(_)) => {
747 // assigning to `P.f` is ok if assigning to `P` is ok
748 self.check_if_assigned_path_is_moved(id, span,
749 use_kind, lp_base);
750 }
751 LpExtend(ref lp_base, _, LpDeref(_)) => {
752 // assigning to `(*P)` requires that `P` be initialized
753 self.check_if_path_is_moved(id, span,
754 use_kind, lp_base);
755 }
756 }
757 }
758
759 fn check_assignment(&self,
760 assignment_id: ast::NodeId,
761 assignment_span: Span,
762 assignee_cmt: mc::cmt<'tcx>,
763 mode: euv::MutateMode) {
764 debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx()));
765
766 // Mutable values can be assigned, as long as they obey loans
767 // and aliasing restrictions:
768 if assignee_cmt.mutbl.is_mutable() {
769 if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) {
770 if mode != euv::Init {
771 check_for_assignment_to_borrowed_path(
772 self, assignment_id, assignment_span, assignee_cmt.clone());
773 mark_variable_as_used_mut(self, assignee_cmt);
774 }
775 }
776 return;
777 }
778
779 // Initializations are OK.
780 if mode == euv::Init {
781 return
782 }
783
784 // For immutable local variables, assignments are legal
785 // if they cannot already have been assigned
786 if self.is_local_variable_or_arg(assignee_cmt.clone()) {
787 assert!(assignee_cmt.mutbl.is_immutable()); // no "const" locals
788 let lp = opt_loan_path(&assignee_cmt).unwrap();
789 self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
790 self.bccx.report_reassigned_immutable_variable(
791 assignment_span,
792 &*lp,
793 assign);
794 false
795 });
796 return;
797 }
798
799 // Otherwise, just a plain error.
800 match assignee_cmt.note {
801 mc::NoteClosureEnv(upvar_id) => {
802 // If this is an `Fn` closure, it simply can't mutate upvars.
803 // If it's an `FnMut` closure, the original variable was declared immutable.
804 // We need to determine which is the case here.
805 let kind = match assignee_cmt.upvar().unwrap().cat {
806 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
807 _ => unreachable!()
808 };
809 if kind == ty::FnUnboxedClosureKind {
810 self.bccx.span_err(
811 assignment_span,
812 &format!("cannot assign to {}",
813 self.bccx.cmt_to_string(&*assignee_cmt))[]);
814 self.bccx.span_help(
815 self.tcx().map.span(upvar_id.closure_expr_id),
816 "consider changing this closure to take self by mutable reference");
817 } else {
818 self.bccx.span_err(
819 assignment_span,
820 &format!("cannot assign to {} {}",
821 assignee_cmt.mutbl.to_user_str(),
822 self.bccx.cmt_to_string(&*assignee_cmt))[]);
823 }
824 }
825 _ => match opt_loan_path(&assignee_cmt) {
826 Some(lp) => {
827 self.bccx.span_err(
828 assignment_span,
829 &format!("cannot assign to {} {} `{}`",
830 assignee_cmt.mutbl.to_user_str(),
831 self.bccx.cmt_to_string(&*assignee_cmt),
832 self.bccx.loan_path_to_string(&*lp))[]);
833 }
834 None => {
835 self.bccx.span_err(
836 assignment_span,
837 &format!("cannot assign to {} {}",
838 assignee_cmt.mutbl.to_user_str(),
839 self.bccx.cmt_to_string(&*assignee_cmt))[]);
840 }
841 }
842 }
843 return;
844
845 fn mark_variable_as_used_mut<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
846 mut cmt: mc::cmt<'tcx>) {
847 //! If the mutability of the `cmt` being written is inherited
848 //! from a local variable, liveness will
849 //! not have been able to detect that this variable's mutability
850 //! is important, so we must add the variable to the
851 //! `used_mut_nodes` table here.
852
853 loop {
854 debug!("mark_variable_as_used_mut(cmt={})", cmt.repr(this.tcx()));
855 match cmt.cat.clone() {
856 mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: id, .. }, .. }) |
857 mc::cat_local(id) => {
858 this.tcx().used_mut_nodes.borrow_mut().insert(id);
859 return;
860 }
861
862 mc::cat_rvalue(..) |
863 mc::cat_static_item |
864 mc::cat_deref(_, _, mc::UnsafePtr(..)) |
865 mc::cat_deref(_, _, mc::Implicit(..)) => {
866 assert_eq!(cmt.mutbl, mc::McDeclared);
867 return;
868 }
869
870 mc::cat_deref(_, _, mc::BorrowedPtr(..)) => {
871 assert_eq!(cmt.mutbl, mc::McDeclared);
872 // We need to drill down to upvar if applicable
873 match cmt.upvar() {
874 Some(b) => cmt = b,
875 None => return
876 }
877 }
878
879 mc::cat_deref(b, _, mc::Unique) => {
880 assert_eq!(cmt.mutbl, mc::McInherited);
881 cmt = b;
882 }
883
884 mc::cat_downcast(b, _) |
885 mc::cat_interior(b, _) => {
886 assert_eq!(cmt.mutbl, mc::McInherited);
887 cmt = b;
888 }
889 }
890 }
891 }
892
893 fn check_for_aliasable_mutable_writes<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
894 span: Span,
895 cmt: mc::cmt<'tcx>) -> bool {
896 //! Safety checks related to writes to aliasable, mutable locations
897
898 let guarantor = cmt.guarantor();
899 debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
900 cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
901 if let mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) = guarantor.cat {
902 // Statically prohibit writes to `&mut` when aliasable
903 check_for_aliasability_violation(this, span, b.clone());
904 }
905
906 return true; // no errors reported
907 }
908
909 fn check_for_aliasability_violation<'a, 'tcx>(this: &CheckLoanCtxt<'a, 'tcx>,
910 span: Span,
911 cmt: mc::cmt<'tcx>)
912 -> bool {
913 match cmt.freely_aliasable(this.tcx()) {
914 None => {
915 return true;
916 }
917 Some(mc::AliasableStaticMut(..)) => {
918 return true;
919 }
920 Some(cause) => {
921 this.bccx.report_aliasability_violation(
922 span,
923 MutabilityViolation,
924 cause);
925 return false;
926 }
927 }
928 }
929
930 fn check_for_assignment_to_borrowed_path<'a, 'tcx>(
931 this: &CheckLoanCtxt<'a, 'tcx>,
932 assignment_id: ast::NodeId,
933 assignment_span: Span,
934 assignee_cmt: mc::cmt<'tcx>)
935 {
936 //! Check for assignments that violate the terms of an
937 //! outstanding loan.
938
939 let loan_path = match opt_loan_path(&assignee_cmt) {
940 Some(lp) => lp,
941 None => { return; /* no loan path, can't be any loans */ }
942 };
943
944 let scope = region::CodeExtent::from_node_id(assignment_id);
945 this.each_in_scope_loan_affecting_path(scope, &*loan_path, |loan| {
946 this.report_illegal_mutation(assignment_span, &*loan_path, loan);
947 false
948 });
949 }
950 }
951
952 pub fn report_illegal_mutation(&self,
953 span: Span,
954 loan_path: &LoanPath<'tcx>,
955 loan: &Loan) {
956 self.bccx.span_err(
957 span,
958 &format!("cannot assign to `{}` because it is borrowed",
959 self.bccx.loan_path_to_string(loan_path))[]);
960 self.bccx.span_note(
961 loan.span,
962 &format!("borrow of `{}` occurs here",
963 self.bccx.loan_path_to_string(loan_path))[]);
964 }
965 }