1 // Copyright 2012-2014 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 /*! See doc.rs for a thorough explanation of the borrow checker */
12
13 #![allow(non_camel_case_types)]
14
15 use middle::dataflow::DataFlowContext;
16 use middle::dataflow::DataFlowOperator;
17 use euv = middle::expr_use_visitor;
18 use mc = middle::mem_categorization;
19 use middle::ty;
20 use util::ppaux::{note_and_explain_region, Repr, UserString};
21
22 use std::cell::{Cell};
23 use std::ops::{BitOr, BitAnd};
24 use std::rc::Rc;
25 use std::strbuf::StrBuf;
26 use syntax::ast;
27 use syntax::ast_map;
28 use syntax::ast_util;
29 use syntax::codemap::Span;
30 use syntax::parse::token;
31 use syntax::visit;
32 use syntax::visit::{Visitor, FnKind};
33 use syntax::ast::{FnDecl, Block, NodeId};
34
35 macro_rules! if_ok(
36 ($inp: expr) => (
37 match $inp {
38 Ok(v) => { v }
39 Err(e) => { return Err(e); }
40 }
41 )
42 )
43
44 pub mod doc;
45
46 pub mod check_loans;
47
48 pub mod gather_loans;
49
50 pub mod move_data;
51
52 pub struct LoanDataFlowOperator;
53
54 /// FIXME(pcwalton): Should just be #[deriving(Clone)], but that doesn't work
55 /// yet on unit structs.
56 impl Clone for LoanDataFlowOperator {
57 fn clone(&self) -> LoanDataFlowOperator {
58 LoanDataFlowOperator
59 }
60 }
61
62 pub type LoanDataFlow<'a> = DataFlowContext<'a, LoanDataFlowOperator>;
63
64 impl<'a> Visitor<()> for BorrowckCtxt<'a> {
65 fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl,
66 b: &Block, s: Span, n: NodeId, _: ()) {
67 borrowck_fn(self, fk, fd, b, s, n);
68 }
69
70 fn visit_item(&mut self, item: &ast::Item, _: ()) {
71 borrowck_item(self, item);
72 }
73 }
74
75 pub fn check_crate(tcx: &ty::ctxt,
76 krate: &ast::Crate) {
77 let mut bccx = BorrowckCtxt {
78 tcx: tcx,
79 stats: @BorrowStats {
80 loaned_paths_same: Cell::new(0),
81 loaned_paths_imm: Cell::new(0),
82 stable_paths: Cell::new(0),
83 guaranteed_paths: Cell::new(0),
84 }
85 };
86
87 visit::walk_crate(&mut bccx, krate, ());
88
89 if tcx.sess.borrowck_stats() {
90 println!("--- borrowck stats ---");
91 println!("paths requiring guarantees: {}",
92 bccx.stats.guaranteed_paths.get());
93 println!("paths requiring loans : {}",
94 make_stat(&bccx, bccx.stats.loaned_paths_same.get()));
95 println!("paths requiring imm loans : {}",
96 make_stat(&bccx, bccx.stats.loaned_paths_imm.get()));
97 println!("stable paths : {}",
98 make_stat(&bccx, bccx.stats.stable_paths.get()));
99 }
100
101 fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str {
102 let stat_f = stat as f64;
103 let total = bccx.stats.guaranteed_paths.get() as f64;
104 format!("{} ({:.0f}%)", stat , stat_f * 100.0 / total)
105 }
106 }
107
108 fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
109 // Gather loans for items. Note that we don't need
110 // to check loans for single expressions. The check
111 // loan step is intended for things that have a data
112 // flow dependent conditions.
113 match item.node {
114 ast::ItemStatic(_, _, ex) => {
115 gather_loans::gather_loans_in_static_initializer(this, ex);
116 }
117 _ => {
118 visit::walk_item(this, item, ());
119 }
120 }
121 }
122
123 fn borrowck_fn(this: &mut BorrowckCtxt,
124 fk: &FnKind,
125 decl: &ast::FnDecl,
126 body: &ast::Block,
127 sp: Span,
128 id: ast::NodeId) {
129 debug!("borrowck_fn(id={})", id);
130
131 // Check the body of fn items.
132 let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
133 let (all_loans, move_data) =
134 gather_loans::gather_loans_in_fn(this, decl, body);
135 let mut loan_dfcx =
136 DataFlowContext::new(this.tcx,
137 LoanDataFlowOperator,
138 id_range,
139 all_loans.len());
140 for (loan_idx, loan) in all_loans.iter().enumerate() {
141 loan_dfcx.add_gen(loan.gen_scope, loan_idx);
142 loan_dfcx.add_kill(loan.kill_scope, loan_idx);
143 }
144 loan_dfcx.propagate(body);
145
146 let flowed_moves = move_data::FlowedMoveData::new(move_data,
147 this.tcx,
148 id_range,
149 body);
150
151 check_loans::check_loans(this, &loan_dfcx, flowed_moves,
152 all_loans.as_slice(), body);
153
154 visit::walk_fn(this, fk, decl, body, sp, id, ());
155 }
156
157 // ----------------------------------------------------------------------
158 // Type definitions
159
160 pub struct BorrowckCtxt<'a> {
161 tcx: &'a ty::ctxt,
162
163 // Statistics:
164 stats: @BorrowStats
165 }
166
167 pub struct BorrowStats {
168 loaned_paths_same: Cell<uint>,
169 loaned_paths_imm: Cell<uint>,
170 stable_paths: Cell<uint>,
171 guaranteed_paths: Cell<uint>,
172 }
173
174 pub type BckResult<T> = Result<T, BckError>;
175
176 #[deriving(Eq)]
177 pub enum PartialTotal {
178 Partial, // Loan affects some portion
179 Total // Loan affects entire path
180 }
181
182 ///////////////////////////////////////////////////////////////////////////
183 // Loans and loan paths
184
185 /// Record of a loan that was issued.
186 pub struct Loan {
187 index: uint,
188 loan_path: Rc<LoanPath>,
189 cmt: mc::cmt,
190 kind: ty::BorrowKind,
191 restrictions: Vec<Restriction>,
192 gen_scope: ast::NodeId,
193 kill_scope: ast::NodeId,
194 span: Span,
195 cause: euv::LoanCause,
196 }
197
198 #[deriving(Eq, TotalEq, Hash)]
199 pub enum LoanPath {
200 LpVar(ast::NodeId), // `x` in doc.rs
201 LpExtend(Rc<LoanPath>, mc::MutabilityCategory, LoanPathElem)
202 }
203
204 #[deriving(Eq, TotalEq, Hash)]
205 pub enum LoanPathElem {
206 LpDeref(mc::PointerKind), // `*LV` in doc.rs
207 LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
208 }
209
210 impl LoanPath {
211 pub fn node_id(&self) -> ast::NodeId {
212 match *self {
213 LpVar(local_id) => local_id,
214 LpExtend(ref base, _, _) => base.node_id()
215 }
216 }
217 }
218
219 pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
220 //! Computes the `LoanPath` (if any) for a `cmt`.
221 //! Note that this logic is somewhat duplicated in
222 //! the method `compute()` found in `gather_loans::restrictions`,
223 //! which allows it to share common loan path pieces as it
224 //! traverses the CMT.
225
226 match cmt.cat {
227 mc::cat_rvalue(..) |
228 mc::cat_static_item |
229 mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => {
230 None
231 }
232
233 mc::cat_local(id) |
234 mc::cat_arg(id) |
235 mc::cat_copied_upvar(mc::CopiedUpvar { upvar_id: id, .. }) |
236 mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => {
237 Some(Rc::new(LpVar(id)))
238 }
239
240 mc::cat_deref(ref cmt_base, _, pk) => {
241 opt_loan_path(cmt_base).map(|lp| {
242 Rc::new(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
243 })
244 }
245
246 mc::cat_interior(ref cmt_base, ik) => {
247 opt_loan_path(cmt_base).map(|lp| {
248 Rc::new(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
249 })
250 }
251
252 mc::cat_downcast(ref cmt_base) |
253 mc::cat_discr(ref cmt_base, _) => {
254 opt_loan_path(cmt_base)
255 }
256 }
257 }
258
259 ///////////////////////////////////////////////////////////////////////////
260 // Restrictions
261 //
262 // Borrowing an lvalue often results in *restrictions* that limit what
263 // can be done with this lvalue during the scope of the loan:
264 //
265 // - `RESTR_MUTATE`: The lvalue may not be modified or `&mut` borrowed.
266 // - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
267 //
268 // In addition, no value which is restricted may be moved. Therefore,
269 // restrictions are meaningful even if the RestrictionSet is empty,
270 // because the restriction against moves is implied.
271
272 pub struct Restriction {
273 loan_path: Rc<LoanPath>,
274 set: RestrictionSet
275 }
276
277 #[deriving(Eq)]
278 pub struct RestrictionSet {
279 bits: u32
280 }
281
282 #[allow(dead_code)] // potentially useful
283 pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000};
284 pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
285 pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0010};
286
287 impl RestrictionSet {
288 pub fn intersects(&self, restr: RestrictionSet) -> bool {
289 (self.bits & restr.bits) != 0
290 }
291 }
292
293 impl BitOr<RestrictionSet,RestrictionSet> for RestrictionSet {
294 fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet {
295 RestrictionSet {bits: self.bits | rhs.bits}
296 }
297 }
298
299 impl BitAnd<RestrictionSet,RestrictionSet> for RestrictionSet {
300 fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet {
301 RestrictionSet {bits: self.bits & rhs.bits}
302 }
303 }
304
305 impl Repr for RestrictionSet {
306 fn repr(&self, _tcx: &ty::ctxt) -> ~str {
307 format!("RestrictionSet(0x{:x})", self.bits as uint)
308 }
309 }
310
311 ///////////////////////////////////////////////////////////////////////////
312 // Errors
313
314 // Errors that can occur
315 #[deriving(Eq)]
316 pub enum bckerr_code {
317 err_mutbl,
318 err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
319 err_borrowed_pointer_too_short(
320 ty::Region, ty::Region, RestrictionSet), // loan, ptr
321 }
322
323 // Combination of an error code and the categorization of the expression
324 // that caused it
325 #[deriving(Eq)]
326 pub struct BckError {
327 span: Span,
328 cause: euv::LoanCause,
329 cmt: mc::cmt,
330 code: bckerr_code
331 }
332
333 pub enum AliasableViolationKind {
334 MutabilityViolation,
335 BorrowViolation(euv::LoanCause)
336 }
337
338 pub enum MovedValueUseKind {
339 MovedInUse,
340 MovedInCapture,
341 }
342
343 ///////////////////////////////////////////////////////////////////////////
344 // Misc
345
346 impl<'a> BorrowckCtxt<'a> {
347 pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
348 -> bool {
349 self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
350 }
351
352 pub fn is_subscope_of(&self, r_sub: ast::NodeId, r_sup: ast::NodeId)
353 -> bool {
354 self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
355 }
356
357 pub fn mc(&self) -> mc::MemCategorizationContext<'a,ty::ctxt> {
358 mc::MemCategorizationContext::new(self.tcx)
359 }
360
361 pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt {
362 match self.mc().cat_expr(expr) {
363 Ok(c) => c,
364 Err(()) => {
365 self.tcx.sess.span_bug(expr.span, "error in mem categorization");
366 }
367 }
368 }
369
370 pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> mc::cmt {
371 match self.mc().cat_expr_unadjusted(expr) {
372 Ok(c) => c,
373 Err(()) => {
374 self.tcx.sess.span_bug(expr.span, "error in mem categorization");
375 }
376 }
377 }
378
379 pub fn cat_expr_autoderefd(&self,
380 expr: &ast::Expr,
381 adj: &ty::AutoAdjustment)
382 -> mc::cmt {
383 let r = match *adj {
384 ty::AutoAddEnv(..) | ty::AutoObject(..) => {
385 // no autoderefs
386 self.mc().cat_expr_unadjusted(expr)
387 }
388
389 ty::AutoDerefRef(
390 ty::AutoDerefRef {
391 autoderefs: autoderefs, ..}) => {
392 self.mc().cat_expr_autoderefd(expr, autoderefs)
393 }
394 };
395
396 match r {
397 Ok(c) => c,
398 Err(()) => {
399 self.tcx.sess.span_bug(expr.span,
400 "error in mem categorization");
401 }
402 }
403 }
404
405 pub fn cat_def(&self,
406 id: ast::NodeId,
407 span: Span,
408 ty: ty::t,
409 def: ast::Def)
410 -> mc::cmt {
411 match self.mc().cat_def(id, span, ty, def) {
412 Ok(c) => c,
413 Err(()) => {
414 self.tcx.sess.span_bug(span, "error in mem categorization");
415 }
416 }
417 }
418
419 pub fn cat_captured_var(&self,
420 closure_id: ast::NodeId,
421 closure_span: Span,
422 upvar_def: ast::Def)
423 -> mc::cmt {
424 // Create the cmt for the variable being borrowed, from the
425 // caller's perspective
426 let var_id = ast_util::def_id_of_def(upvar_def).node;
427 let var_ty = ty::node_id_to_type(self.tcx, var_id);
428 self.cat_def(closure_id, closure_span, var_ty, upvar_def)
429 }
430
431 pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt {
432 Rc::new(mc::cmt_ {
433 cat: mc::cat_discr(cmt.clone(), match_id),
434 mutbl: cmt.mutbl.inherit(),
435 ..*cmt
436 })
437 }
438
439 pub fn cat_pattern(&self,
440 cmt: mc::cmt,
441 pat: &ast::Pat,
442 op: |mc::cmt, &ast::Pat|) {
443 let r = self.mc().cat_pattern(cmt, pat, |_,x,y| op(x,y));
444 assert!(r.is_ok());
445 }
446
447 pub fn report(&self, err: BckError) {
448 self.span_err(
449 err.span,
450 self.bckerr_to_str(&err));
451 self.note_and_explain_bckerr(err);
452 }
453
454 pub fn report_use_of_moved_value(&self,
455 use_span: Span,
456 use_kind: MovedValueUseKind,
457 lp: &LoanPath,
458 move: &move_data::Move,
459 moved_lp: &LoanPath) {
460 let verb = match use_kind {
461 MovedInUse => "use",
462 MovedInCapture => "capture",
463 };
464
465 match move.kind {
466 move_data::Declared => {
467 self.tcx.sess.span_err(
468 use_span,
469 format!("{} of possibly uninitialized variable: `{}`",
470 verb,
471 self.loan_path_to_str(lp)));
472 }
473 _ => {
474 let partially = if lp == moved_lp {""} else {"partially "};
475 self.tcx.sess.span_err(
476 use_span,
477 format!("{} of {}moved value: `{}`",
478 verb,
479 partially,
480 self.loan_path_to_str(lp)));
481 }
482 }
483
484 match move.kind {
485 move_data::Declared => {}
486
487 move_data::MoveExpr => {
488 let (expr_ty, expr_span) = match self.tcx.map.find(move.id) {
489 Some(ast_map::NodeExpr(expr)) => {
490 (ty::expr_ty_adjusted(self.tcx, expr), expr.span)
491 }
492 r => self.tcx.sess.bug(format!("MoveExpr({:?}) maps to {:?}, not Expr",
493 move.id, r))
494 };
495 let suggestion = move_suggestion(self.tcx, expr_ty,
496 "moved by default (use `copy` to override)");
497 self.tcx.sess.span_note(
498 expr_span,
499 format!("`{}` moved here because it has type `{}`, which is {}",
500 self.loan_path_to_str(moved_lp),
501 expr_ty.user_string(self.tcx), suggestion));
502 }
503
504 move_data::MovePat => {
505 let pat_ty = ty::node_id_to_type(self.tcx, move.id);
506 self.tcx.sess.span_note(self.tcx.map.span(move.id),
507 format!("`{}` moved here because it has type `{}`, \
508 which is moved by default (use `ref` to override)",
509 self.loan_path_to_str(moved_lp),
510 pat_ty.user_string(self.tcx)));
511 }
512
513 move_data::Captured => {
514 let (expr_ty, expr_span) = match self.tcx.map.find(move.id) {
515 Some(ast_map::NodeExpr(expr)) => {
516 (ty::expr_ty_adjusted(self.tcx, expr), expr.span)
517 }
518 r => self.tcx.sess.bug(format!("Captured({:?}) maps to {:?}, not Expr",
519 move.id, r))
520 };
521 let suggestion = move_suggestion(self.tcx, expr_ty,
522 "moved by default (make a copy and \
523 capture that instead to override)");
524 self.tcx.sess.span_note(
525 expr_span,
526 format!("`{}` moved into closure environment here because it \
527 has type `{}`, which is {}",
528 self.loan_path_to_str(moved_lp),
529 expr_ty.user_string(self.tcx), suggestion));
530 }
531 }
532
533 fn move_suggestion(tcx: &ty::ctxt, ty: ty::t, default_msg: &'static str)
534 -> &'static str {
535 match ty::get(ty).sty {
536 ty::ty_closure(box ty::ClosureTy {
537 store: ty::RegionTraitStore(..),
538 ..
539 }) =>
540 "a non-copyable stack closure (capture it in a new closure, \
541 e.g. `|x| f(x)`, to override)",
542 _ if ty::type_moves_by_default(tcx, ty) =>
543 "non-copyable (perhaps you meant to use clone()?)",
544 _ => default_msg,
545 }
546 }
547 }
548
549 pub fn report_reassigned_immutable_variable(&self,
550 span: Span,
551 lp: &LoanPath,
552 assign:
553 &move_data::Assignment) {
554 self.tcx.sess.span_err(
555 span,
556 format!("re-assignment of immutable variable `{}`",
557 self.loan_path_to_str(lp)));
558 self.tcx.sess.span_note(
559 assign.span,
560 format!("prior assignment occurs here"));
561 }
562
563 pub fn span_err(&self, s: Span, m: &str) {
564 self.tcx.sess.span_err(s, m);
565 }
566
567 pub fn span_note(&self, s: Span, m: &str) {
568 self.tcx.sess.span_note(s, m);
569 }
570
571 pub fn span_end_note(&self, s: Span, m: &str) {
572 self.tcx.sess.span_end_note(s, m);
573 }
574
575 pub fn bckerr_to_str(&self, err: &BckError) -> ~str {
576 match err.code {
577 err_mutbl => {
578 let descr = match opt_loan_path(&err.cmt) {
579 None => format!("{} {}",
580 err.cmt.mutbl.to_user_str(),
581 self.cmt_to_str(&*err.cmt)),
582 Some(lp) => format!("{} {} `{}`",
583 err.cmt.mutbl.to_user_str(),
584 self.cmt_to_str(&*err.cmt),
585 self.loan_path_to_str(&*lp)),
586 };
587
588 match err.cause {
589 euv::ClosureCapture(_) => {
590 format!("closure cannot assign to {}", descr)
591 }
592 euv::OverloadedOperator |
593 euv::AddrOf |
594 euv::RefBinding |
595 euv::AutoRef => {
596 format!("cannot borrow {} as mutable", descr)
597 }
598 euv::ClosureInvocation => {
599 self.tcx.sess.span_bug(err.span,
600 "err_mutbl with a closure invocation");
601 }
602 }
603 }
604 err_out_of_scope(..) => {
605 let msg = match opt_loan_path(&err.cmt) {
606 None => format!("borrowed value"),
607 Some(lp) => format!("`{}`", self.loan_path_to_str(&*lp)),
608 };
609 format!("{} does not live long enough", msg)
610 }
611 err_borrowed_pointer_too_short(..) => {
612 let descr = match opt_loan_path(&err.cmt) {
613 Some(lp) => format!("`{}`", self.loan_path_to_str(&*lp)),
614 None => self.cmt_to_str(&*err.cmt),
615 };
616
617 format!("lifetime of {} is too short to guarantee \
618 its contents can be safely reborrowed",
619 descr)
620 }
621 }
622 }
623
624 pub fn report_aliasability_violation(&self,
625 span: Span,
626 kind: AliasableViolationKind,
627 cause: mc::AliasableReason) {
628 let prefix = match kind {
629 MutabilityViolation => {
630 "cannot assign to data"
631 }
632 BorrowViolation(euv::ClosureCapture(_)) => {
633 // I don't think we can get aliasability violations
634 // with closure captures, so no need to come up with a
635 // good error message. The reason this cannot happen
636 // is because we only capture local variables in
637 // closures, and those are never aliasable.
638 self.tcx.sess.span_bug(
639 span,
640 "aliasability violation with closure");
641 }
642 BorrowViolation(euv::OverloadedOperator) |
643 BorrowViolation(euv::AddrOf) |
644 BorrowViolation(euv::AutoRef) |
645 BorrowViolation(euv::RefBinding) => {
646 "cannot borrow data mutably"
647 }
648
649 BorrowViolation(euv::ClosureInvocation) => {
650 "closure invocation"
651 }
652 };
653
654 match cause {
655 mc::AliasableOther => {
656 self.tcx.sess.span_err(
657 span,
658 format!("{} in an aliasable location", prefix));
659 }
660 mc::AliasableStatic(..) |
661 mc::AliasableStaticMut(..) => {
662 self.tcx.sess.span_err(
663 span,
664 format!("{} in a static location", prefix));
665 }
666 mc::AliasableManaged => {
667 self.tcx.sess.span_err(
668 span,
669 format!("{} in a `@` pointer", prefix));
670 }
671 mc::AliasableBorrowed => {
672 self.tcx.sess.span_err(
673 span,
674 format!("{} in a `&` reference", prefix));
675 }
676 }
677 }
678
679 pub fn note_and_explain_bckerr(&self, err: BckError) {
680 let code = err.code;
681 match code {
682 err_mutbl(..) => { }
683
684 err_out_of_scope(super_scope, sub_scope) => {
685 note_and_explain_region(
686 self.tcx,
687 "reference must be valid for ",
688 sub_scope,
689 "...");
690 note_and_explain_region(
691 self.tcx,
692 "...but borrowed value is only valid for ",
693 super_scope,
694 "");
695 }
696
697 err_borrowed_pointer_too_short(loan_scope, ptr_scope, _) => {
698 let descr = match opt_loan_path(&err.cmt) {
699 Some(lp) => format!("`{}`", self.loan_path_to_str(&*lp)),
700 None => self.cmt_to_str(&*err.cmt),
701 };
702 note_and_explain_region(
703 self.tcx,
704 format!("{} would have to be valid for ", descr),
705 loan_scope,
706 "...");
707 note_and_explain_region(
708 self.tcx,
709 format!("...but {} is only valid for ", descr),
710 ptr_scope,
711 "");
712 }
713 }
714 }
715
716 pub fn append_loan_path_to_str(&self,
717 loan_path: &LoanPath,
718 out: &mut StrBuf) {
719 match *loan_path {
720 LpVar(id) => {
721 out.push_str(ty::local_var_name_str(self.tcx, id).get());
722 }
723
724 LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
725 self.append_autoderefd_loan_path_to_str(&**lp_base, out);
726 match fname {
727 mc::NamedField(fname) => {
728 out.push_char('.');
729 out.push_str(token::get_name(fname).get());
730 }
731 mc::PositionalField(idx) => {
732 out.push_char('#'); // invent a notation here
733 out.push_str(idx.to_str());
734 }
735 }
736 }
737
738 LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
739 self.append_autoderefd_loan_path_to_str(&**lp_base, out);
740 out.push_str("[..]");
741 }
742
743 LpExtend(ref lp_base, _, LpDeref(_)) => {
744 out.push_char('*');
745 self.append_loan_path_to_str(&**lp_base, out);
746 }
747 }
748 }
749
750 pub fn append_autoderefd_loan_path_to_str(&self,
751 loan_path: &LoanPath,
752 out: &mut StrBuf) {
753 match *loan_path {
754 LpExtend(ref lp_base, _, LpDeref(_)) => {
755 // For a path like `(*x).f` or `(*x)[3]`, autoderef
756 // rules would normally allow users to omit the `*x`.
757 // So just serialize such paths to `x.f` or x[3]` respectively.
758 self.append_autoderefd_loan_path_to_str(&**lp_base, out)
759 }
760
761 LpVar(..) | LpExtend(_, _, LpInterior(..)) => {
762 self.append_loan_path_to_str(loan_path, out)
763 }
764 }
765 }
766
767 pub fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str {
768 let mut result = StrBuf::new();
769 self.append_loan_path_to_str(loan_path, &mut result);
770 result.into_owned()
771 }
772
773 pub fn cmt_to_str(&self, cmt: &mc::cmt_) -> ~str {
774 self.mc().cmt_to_str(cmt)
775 }
776 }
777
778 impl DataFlowOperator for LoanDataFlowOperator {
779 #[inline]
780 fn initial_value(&self) -> bool {
781 false // no loans in scope by default
782 }
783
784 #[inline]
785 fn join(&self, succ: uint, pred: uint) -> uint {
786 succ | pred // loans from both preds are in scope
787 }
788 }
789
790 impl Repr for Loan {
791 fn repr(&self, tcx: &ty::ctxt) -> ~str {
792 format!("Loan_{:?}({}, {:?}, {:?}-{:?}, {})",
793 self.index,
794 self.loan_path.repr(tcx),
795 self.kind,
796 self.gen_scope,
797 self.kill_scope,
798 self.restrictions.repr(tcx))
799 }
800 }
801
802 impl Repr for Restriction {
803 fn repr(&self, tcx: &ty::ctxt) -> ~str {
804 format!("Restriction({}, {:x})",
805 self.loan_path.repr(tcx),
806 self.set.bits as uint)
807 }
808 }
809
810 impl Repr for LoanPath {
811 fn repr(&self, tcx: &ty::ctxt) -> ~str {
812 match self {
813 &LpVar(id) => {
814 format!("$({})", tcx.map.node_to_str(id))
815 }
816
817 &LpExtend(ref lp, _, LpDeref(_)) => {
818 format!("{}.*", lp.repr(tcx))
819 }
820
821 &LpExtend(ref lp, _, LpInterior(ref interior)) => {
822 format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
823 }
824 }
825 }
826 }
827
librustc/middle/borrowck/mod.rs:204:31-204:31 -enum- definition:
pub enum LoanPathElem {
LpDeref(mc::PointerKind), // `*LV` in doc.rs
LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
references:- 7205: pub enum LoanPathElem {
librustc/middle/borrowck/gather_loans/restrictions.rs:
174: mc: mc::MutabilityCategory,
175: elem: LoanPathElem,
176: restrictions: RestrictionSet) -> RestrictionResult {
librustc/middle/borrowck/mod.rs:
205: pub enum LoanPathElem {
librustc/middle/borrowck/mod.rs:533:8-533:8 -fn- definition:
fn move_suggestion(tcx: &ty::ctxt, ty: ty::t, default_msg: &'static str)
-> &'static str {
match ty::get(ty).sty {
references:- 2520: };
521: let suggestion = move_suggestion(self.tcx, expr_ty,
522: "moved by default (make a copy and \
librustc/middle/borrowck/mod.rs:198:31-198:31 -enum- definition:
pub enum LoanPath {
LpVar(ast::NodeId), // `x` in doc.rs
LpExtend(Rc<LoanPath>, mc::MutabilityCategory, LoanPathElem)
references:- 40librustc/middle/borrowck/check_loans.rs:
librustc/middle/borrowck/gather_loans/mod.rs:
librustc/middle/borrowck/gather_loans/restrictions.rs:
librustc/middle/borrowck/gather_loans/gather_moves.rs:
librustc/middle/borrowck/move_data.rs:
librustc/middle/borrowck/mod.rs:
librustc/middle/borrowck/mod.rs:101:4-101:4 -fn- definition:
fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> ~str {
let stat_f = stat as f64;
let total = bccx.stats.guaranteed_paths.get() as f64;
references:- 395: println!("paths requiring imm loans : {}",
96: make_stat(&bccx, bccx.stats.loaned_paths_imm.get()));
97: println!("stable paths : {}",
98: make_stat(&bccx, bccx.stats.stable_paths.get()));
99: }
librustc/middle/borrowck/mod.rs:166:1-166:1 -struct- definition:
pub struct BorrowStats {
loaned_paths_same: Cell<uint>,
loaned_paths_imm: Cell<uint>,
references:- 2163: // Statistics:
164: stats: @BorrowStats
165: }
librustc/middle/borrowck/mod.rs:185:38-185:38 -struct- definition:
/// Record of a loan that was issued.
pub struct Loan {
index: uint,
references:- 16librustc/middle/borrowck/gather_loans/mod.rs:
308: Loan {
309: index: self.all_loans.len(),
librustc/middle/borrowck/mod.rs:
790: impl Repr for Loan {
791: fn repr(&self, tcx: &ty::ctxt) -> ~str {
librustc/middle/borrowck/check_loans.rs:
211: loan1: &Loan,
212: loan2: &Loan,
213: old_loan: &Loan,
214: new_loan: &Loan)
215: -> bool {
--
670: loan_path: &LoanPath,
671: loan: &Loan) {
672: self.bccx.span_err(
librustc/middle/borrowck/gather_loans/mod.rs:
63: move_error_collector: move_error::MoveErrorCollector,
64: all_loans: Vec<Loan>,
65: item_ub: ast::NodeId,
librustc/middle/borrowck/check_loans.rs:
39: move_data: move_data::FlowedMoveData<'a>,
40: all_loans: &'a [Loan],
41: }
librustc/middle/borrowck/mod.rs:159:1-159:1 -struct- definition:
pub struct BorrowckCtxt<'a> {
tcx: &'a ty::ctxt,
// Statistics:
references:- 2876: krate: &ast::Crate) {
77: let mut bccx = BorrowckCtxt {
78: tcx: tcx,
--
123: fn borrowck_fn(this: &mut BorrowckCtxt,
124: fk: &FnKind,
--
346: impl<'a> BorrowckCtxt<'a> {
347: pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
librustc/middle/borrowck/check_loans.rs:
36: struct CheckLoanCtxt<'a> {
37: bccx: &'a BorrowckCtxt<'a>,
38: dfcx_loans: &'a LoanDataFlow<'a>,
--
68: pub fn check_loans(bccx: &BorrowckCtxt,
69: dfcx_loans: &LoanDataFlow,
librustc/middle/borrowck/gather_loans/mod.rs:
60: struct GatherLoanCtxt<'a> {
61: bccx: &'a BorrowckCtxt<'a>,
62: move_data: move_data::MoveData,
--
355: fn check_mutability(bccx: &BorrowckCtxt,
356: borrow_span: Span,
--
505: pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::Expr) {
librustc/middle/borrowck/gather_loans/lifetime.rs:
49: struct GuaranteeLifetimeContext<'a> {
50: bccx: &'a BorrowckCtxt<'a>,
librustc/middle/borrowck/gather_loans/restrictions.rs:
29: pub fn compute_restrictions(bccx: &BorrowckCtxt,
30: span: Span,
--
49: struct RestrictionsContext<'a> {
50: bccx: &'a BorrowckCtxt<'a>,
51: span: Span,
librustc/middle/borrowck/gather_loans/gather_moves.rs:
79: fn gather_move(bccx: &BorrowckCtxt,
80: move_data: &MoveData,
--
125: fn check_and_get_illegal_move_origin(bccx: &BorrowckCtxt,
126: cmt: &mc::cmt) -> Option<mc::cmt> {
librustc/middle/borrowck/gather_loans/move_error.rs:
36: pub fn report_potential_errors(&self, bccx: &BorrowckCtxt) {
37: report_move_errors(bccx, self.errors.borrow().deref())
--
79: fn report_move_errors(bccx: &BorrowckCtxt, errors: &Vec<MoveError>) {
80: let grouped_errors = group_errors_with_same_origin(errors);
--
155: fn note_move_destination(bccx: &BorrowckCtxt,
156: move_to_span: codemap::Span,
librustc/middle/borrowck/gather_loans/gather_moves.rs:
110: pub fn gather_assignment(bccx: &BorrowckCtxt,
111: move_data: &MoveData,
librustc/middle/borrowck/mod.rs:51:1-51:1 -struct- definition:
pub struct LoanDataFlowOperator;
/// FIXME(pcwalton): Should just be #[deriving(Clone)], but that doesn't work
/// yet on unit structs.
references:- 462: pub type LoanDataFlow<'a> = DataFlowContext<'a, LoanDataFlowOperator>;
--
778: impl DataFlowOperator for LoanDataFlowOperator {
779: #[inline]
librustc/middle/borrowck/mod.rs:315:16-315:16 -enum- definition:
pub enum bckerr_code {
err_mutbl,
err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
references:- 5314: // Errors that can occur
316: pub enum bckerr_code {
librustc/middle/borrowck/gather_loans/lifetime.rs:
198: fn report_error(&self, code: bckerr_code) {
199: self.bccx.report(BckError { cmt: self.cmt_original.clone(),
librustc/middle/borrowck/mod.rs:
329: cmt: mc::cmt,
330: code: bckerr_code
331: }
librustc/middle/borrowck/mod.rs:271:1-271:1 -struct- definition:
pub struct Restriction {
loan_path: Rc<LoanPath>,
set: RestrictionSet
references:- 6librustc/middle/borrowck/gather_loans/restrictions.rs:
180: let lp = Rc::new(LpExtend(base_lp, mc, elem));
181: base_vec.push(Restriction {
182: loan_path: lp.clone(),
librustc/middle/borrowck/mod.rs:
802: impl Repr for Restriction {
803: fn repr(&self, tcx: &ty::ctxt) -> ~str {
librustc/middle/borrowck/check_loans.rs:
127: loan_path: &LoanPath,
128: op: |&Loan, &Restriction| -> bool)
129: -> bool {
librustc/middle/borrowck/gather_loans/restrictions.rs:
25: Safe,
26: SafeIf(Rc<LoanPath>, Vec<Restriction>)
27: }
librustc/middle/borrowck/mod.rs:
190: kind: ty::BorrowKind,
191: restrictions: Vec<Restriction>,
192: gen_scope: ast::NodeId,
librustc/middle/borrowck/mod.rs:337:1-337:1 -enum- definition:
pub enum MovedValueUseKind {
MovedInUse,
MovedInCapture,
references:- 2librustc/middle/borrowck/check_loans.rs:
364: span: Span,
365: use_kind: MovedValueUseKind,
366: lp: &Rc<LoanPath>) {
librustc/middle/borrowck/mod.rs:
455: use_span: Span,
456: use_kind: MovedValueUseKind,
457: lp: &LoanPath,
librustc/middle/borrowck/mod.rs:218:1-218:1 -fn- definition:
pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
//! Computes the `LoanPath` (if any) for a `cmt`.
//! Note that this logic is somewhat duplicated in
references:- 13577: err_mutbl => {
578: let descr = match opt_loan_path(&err.cmt) {
579: None => format!("{} {}",
--
604: err_out_of_scope(..) => {
605: let msg = match opt_loan_path(&err.cmt) {
606: None => format!("borrowed value"),
--
697: err_borrowed_pointer_too_short(loan_scope, ptr_scope, _) => {
698: let descr = match opt_loan_path(&err.cmt) {
699: Some(lp) => format!("`{}`", self.loan_path_to_str(&*lp)),
librustc/middle/borrowck/check_loans.rs:
423: // Otherwise, just a plain error.
424: match opt_loan_path(&cmt) {
425: Some(lp) => {
--
544: let loan_path = match opt_loan_path(&cmt) {
545: Some(lp) => lp,
--
824: debug!("path cmt={}", cmt.repr(this.tcx()));
825: for lp in opt_loan_path(&cmt).iter() {
826: this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp);
librustc/middle/borrowck/gather_loans/mod.rs:
136: match opt_loan_path(&assignee_cmt) {
137: Some(lp) => {
librustc/middle/borrowck/gather_loans/gather_moves.rs:
99: match opt_loan_path(&move_info.cmt) {
100: Some(loan_path) => {
librustc/middle/borrowck/mod.rs:
611: err_borrowed_pointer_too_short(..) => {
612: let descr = match opt_loan_path(&err.cmt) {
613: Some(lp) => format!("`{}`", self.loan_path_to_str(&*lp)),
librustc/middle/borrowck/mod.rs:61:1-61:1 -NK_AS_STR_TODO- definition:
pub type LoanDataFlow<'a> = DataFlowContext<'a, LoanDataFlowOperator>;
impl<'a> Visitor<()> for BorrowckCtxt<'a> {
fn visit_fn(&mut self, fk: &FnKind, fd: &FnDecl,
references:- 2librustc/middle/borrowck/check_loans.rs:
37: bccx: &'a BorrowckCtxt<'a>,
38: dfcx_loans: &'a LoanDataFlow<'a>,
39: move_data: move_data::FlowedMoveData<'a>,
--
68: pub fn check_loans(bccx: &BorrowckCtxt,
69: dfcx_loans: &LoanDataFlow,
70: move_data: move_data::FlowedMoveData,
librustc/middle/borrowck/mod.rs:277:16-277:16 -struct- definition:
pub struct RestrictionSet {
bits: u32
}
references:- 34librustc/middle/borrowck/gather_loans/mod.rs:
librustc/middle/borrowck/gather_loans/restrictions.rs:
librustc/middle/borrowck/mod.rs:
librustc/middle/borrowck/mod.rs:325:16-325:16 -struct- definition:
pub struct BckError {
span: Span,
cause: euv::LoanCause,
references:- 15librustc/middle/borrowck/gather_loans/mod.rs:
379: if !cmt.mutbl.is_mutable() {
380: Err(bccx.report(BckError { span: borrow_span,
381: cause: cause,
librustc/middle/borrowck/gather_loans/lifetime.rs:
198: fn report_error(&self, code: bckerr_code) {
199: self.bccx.report(BckError { cmt: self.cmt_original.clone(),
200: span: self.span,
librustc/middle/borrowck/gather_loans/restrictions.rs:
132: self.bccx.report(
133: BckError {
134: span: self.span,
--
147: self.bccx.report(
148: BckError {
149: span: self.span,
librustc/middle/borrowck/mod.rs:
324: // that caused it
326: pub struct BckError {
--
679: pub fn note_and_explain_bckerr(&self, err: BckError) {
680: let code = err.code;
librustc/middle/borrowck/mod.rs:176:16-176:16 -enum- definition:
pub enum PartialTotal {
Partial, // Loan affects some portion
Total // Loan affects entire path
references:- 3177: pub enum PartialTotal {