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.
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.
11 // ----------------------------------------------------------------------
14 // The borrow check proceeds in two phases. In phase one, we gather the full
15 // set of loans that are required at any point. These are sorted according to
16 // their associated scopes. In phase two, checking loans, we will then make
17 // sure that all of these loans are honored.
19 use middle::borrowck::*;
20 use middle::borrowck::move_data::MoveData;
21 use mc = middle::mem_categorization;
24 use middle::ty::{ty_region};
26 use middle::typeck::MethodCall;
27 use util::common::indenter;
28 use util::ppaux::{Repr};
32 use syntax::ast_util::IdRange;
33 use syntax::codemap::Span;
34 use syntax::print::pprust;
36 use syntax::visit::{Visitor, FnKind};
37 use syntax::ast::{Expr, FnDecl, Block, NodeId, Stmt, Pat, Local};
43 /// Context used while gathering loans:
45 /// - `bccx`: the borrow check context
46 /// - `item_ub`: the id of the block for the enclosing fn/method item
47 /// - `root_ub`: the id of the outermost block for which we can root
48 /// an `@T`. This is the id of the innermost enclosing
49 /// loop or function body.
51 /// The role of `root_ub` is to prevent us from having to accumulate
52 /// vectors of rooted items at runtime. Consider this case:
54 /// fn foo(...) -> int {
55 /// let mut ptr: ∫
57 /// let x: @int = ...;
63 /// If we are not careful here, we would infer the scope of the borrow `&*x`
64 /// to be the body of the function `foo()` as a whole. We would then
65 /// have root each `@int` that is produced, which is an unbounded number.
66 /// No good. Instead what will happen is that `root_ub` will be set to the
67 /// body of the while loop and we will refuse to root the pointer `&*x`
68 /// because it would have to be rooted for a region greater than `root_ub`.
69 struct GatherLoanCtxt<'a> {
70 bccx: &'a BorrowckCtxt<'a>,
72 move_data: move_data::MoveData,
75 repeating_ids: Vec<ast::NodeId> }
77 impl<'a> visit::Visitor<()> for GatherLoanCtxt<'a> {
78 fn visit_expr(&mut self, ex: &Expr, _: ()) {
79 gather_loans_in_expr(self, ex);
81 fn visit_block(&mut self, b: &Block, _: ()) {
82 gather_loans_in_block(self, b);
85 /// Do not visit closures or fn items here, the outer loop in
86 /// borrowck/mod will visit them for us in turn.
87 fn visit_fn(&mut self, _: &FnKind, _: &FnDecl, _: &Block,
88 _: Span, _: NodeId, _: ()) {}
90 fn visit_stmt(&mut self, s: &Stmt, _: ()) {
91 visit::walk_stmt(self, s, ());
93 fn visit_pat(&mut self, p: &Pat, _: ()) {
94 add_pat_to_id_range(self, p);
96 fn visit_local(&mut self, l: &Local, _: ()) {
97 gather_loans_in_local(self, l);
100 // #7740: Do not visit items here, not even fn items nor methods
101 // of impl items; the outer loop in borrowck/mod will visit them
102 // for us in turn. Thus override visit_item's walk with a no-op.
103 fn visit_item(&mut self, _: &ast::Item, _: ()) {}
106 fn add_pat_to_id_range(this: &mut GatherLoanCtxt,
108 // NB: This visitor function just adds the pat ids into the id
109 // range. We gather loans that occur in patterns using the
110 // `gather_pat()` method below. Eventually these two should be
112 this.id_range.add(p.id);
113 visit::walk_pat(this, p, ());
116 pub fn gather_loans_in_fn(bccx: &BorrowckCtxt, decl: &ast::FnDecl, body: &ast::Block)
117 -> (IdRange, Vec<Loan>, move_data::MoveData) {
118 let mut glcx = GatherLoanCtxt {
120 id_range: IdRange::max(),
121 all_loans: Vec::new(),
123 repeating_ids: vec!(body.id),
124 move_data: MoveData::new()
126 glcx.gather_fn_arg_patterns(decl, body);
128 glcx.visit_block(body, ());
129 let GatherLoanCtxt { id_range, all_loans, move_data, .. } = glcx;
130 (id_range, all_loans, move_data)
133 fn gather_loans_in_block(this: &mut GatherLoanCtxt,
135 this.id_range.add(blk.id);
136 visit::walk_block(this, blk, ());
139 fn gather_loans_in_local(this: &mut GatherLoanCtxt,
140 local: &ast::Local) {
143 // Variable declarations without initializers are considered "moves":
144 let tcx = this.bccx.tcx;
145 pat_util::pat_bindings(tcx.def_map, local.pat, |_, id, span, _| {
146 gather_moves::gather_decl(this.bccx,
154 // Variable declarations with initializers are considered "assigns",
155 // which is handled by `gather_pat`:
156 let init_cmt = this.bccx.cat_expr(init);
157 this.gather_pat(init_cmt, local.pat, None);
161 visit::walk_local(this, local, ());
164 pub fn gather_loans_in_static_initializer(bccx: &mut BorrowckCtxt, expr: &ast::Expr) {
166 debug!("gather_loans_in_item(expr={})", expr.repr(bccx.tcx));
168 let mut glcx = GatherLoanCtxt {
170 id_range: IdRange::max(),
171 all_loans: Vec::new(),
173 repeating_ids: vec!(expr.id),
174 move_data: MoveData::new()
177 // FIXME #13005 This should also walk the
180 ast::ExprAddrOf(..) => {
181 glcx.visit_expr(expr, ());
187 fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
189 let bccx = this.bccx;
192 debug!("gather_loans_in_expr(expr={:?}/{})",
193 ex.id, pprust::expr_to_str(ex));
195 this.id_range.add(ex.id);
197 // If this expression is borrowed, have to ensure it remains valid:
198 for &adjustments in tcx.adjustments.borrow().find(&ex.id).iter() {
199 this.guarantee_adjustments(ex, *adjustments);
202 // If this expression is a move, gather it:
203 if this.bccx.is_move(ex.id) {
204 let cmt = this.bccx.cat_expr(ex);
205 gather_moves::gather_move_from_expr(
206 this.bccx, &this.move_data, ex, cmt);
209 // Special checks for various kinds of expressions:
210 let method_map = this.bccx.method_map.borrow();
212 ast::ExprAddrOf(mutbl, base) => {
213 let base_cmt = this.bccx.cat_expr(base);
215 // make sure that the thing we are pointing out stays valid
216 // for the lifetime `scope_r` of the resulting ptr:
217 let expr_ty = ty::expr_ty(tcx, ex);
218 if !ty::type_is_bot(expr_ty) {
219 let scope_r = ty_region(tcx, ex.span, expr_ty);
220 this.guarantee_valid(ex.id,
227 visit::walk_expr(this, ex, ());
230 ast::ExprAssign(l, _) => {
231 with_assignee_loan_path(
233 |lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
234 ex.id, ex.span, lp, l.id));
235 visit::walk_expr(this, ex, ());
238 ast::ExprAssignOp(_, l, _) => {
239 with_assignee_loan_path(
241 |lp| gather_moves::gather_move_and_assignment(this.bccx, &this.move_data,
242 ex.id, ex.span, lp, l.id));
243 visit::walk_expr(this, ex, ());
246 ast::ExprMatch(ex_v, ref arms) => {
247 let cmt = this.bccx.cat_expr(ex_v);
248 for arm in arms.iter() {
249 for pat in arm.pats.iter() {
250 this.gather_pat(cmt, *pat, Some((arm.body.id, ex.id)));
253 visit::walk_expr(this, ex, ());
256 ast::ExprIndex(_, arg) |
257 ast::ExprBinary(_, _, arg)
258 if method_map.contains_key(&MethodCall::expr(ex.id)) => {
259 // Arguments in method calls are always passed by ref.
261 // Currently these do not use adjustments, so we have to
262 // hardcode this check here (note that the receiver DOES use
264 let scope_r = ty::ReScope(ex.id);
265 let arg_cmt = this.bccx.cat_expr(arg);
266 this.guarantee_valid(arg.id,
272 visit::walk_expr(this, ex, ());
275 // see explanation attached to the `root_ub` field:
276 ast::ExprWhile(cond, body) => {
277 // during the condition, can only root for the condition
278 this.push_repeating_id(cond.id);
279 this.visit_expr(cond, ());
280 this.pop_repeating_id(cond.id);
282 // during body, can only root for the body
283 this.push_repeating_id(body.id);
284 this.visit_block(body, ());
285 this.pop_repeating_id(body.id);
288 // see explanation attached to the `root_ub` field:
289 ast::ExprLoop(body, _) => {
290 this.push_repeating_id(body.id);
291 visit::walk_expr(this, ex, ());
292 this.pop_repeating_id(body.id);
295 ast::ExprFnBlock(..) | ast::ExprProc(..) => {
296 gather_moves::gather_captures(this.bccx, &this.move_data, ex);
297 this.guarantee_captures(ex);
298 visit::walk_expr(this, ex, ());
301 ast::ExprInlineAsm(ref ia) => {
302 for &(_, out) in ia.outputs.iter() {
303 with_assignee_loan_path(
305 |lp| gather_moves::gather_assignment(this.bccx, &this.move_data,
306 ex.id, ex.span, lp, out.id));
308 visit::walk_expr(this, ex, ());
312 visit::walk_expr(this, ex, ());
317 fn with_assignee_loan_path(bccx: &BorrowckCtxt, expr: &ast::Expr, op: |@LoanPath|) {
318 let cmt = bccx.cat_expr(expr);
319 match opt_loan_path(cmt) {
322 // This can occur with e.g. `*foo() = 5`. In such
323 // cases, there is no need to check for conflicts
324 // with moves etc, just ignore.
329 impl<'a> GatherLoanCtxt<'a> {
330 pub fn tcx(&self) -> &'a ty::ctxt { self.bccx.tcx }
332 pub fn push_repeating_id(&mut self, id: ast::NodeId) {
333 self.repeating_ids.push(id);
336 pub fn pop_repeating_id(&mut self, id: ast::NodeId) {
337 let popped = self.repeating_ids.pop().unwrap();
338 assert_eq!(id, popped);
341 pub fn guarantee_autoderefs(&mut self,
344 let method_map = self.bccx.method_map.borrow();
345 for i in range(0, autoderefs) {
346 match method_map.find(&MethodCall::autoderef(expr.id, i as u32)) {
348 // Treat overloaded autoderefs as if an AutoRef adjustment
349 // was applied on the base type, as that is always the case.
350 let mut mc = self.bccx.mc();
351 let cmt = match mc.cat_expr_autoderefd(expr, i) {
353 Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
355 let self_ty = *ty::ty_fn_args(method.ty).get(0);
356 let (m, r) = match ty::get(self_ty).sty {
357 ty::ty_rptr(r, ref m) => (m.mutbl, r),
358 _ => self.tcx().sess.span_bug(expr.span,
359 format!("bad overloaded deref type {}",
360 method.ty.repr(self.tcx())))
362 self.guarantee_valid(expr.id,
374 pub fn guarantee_adjustments(&mut self,
376 adjustment: &ty::AutoAdjustment) {
377 debug!("guarantee_adjustments(expr={}, adjustment={:?})",
378 expr.repr(self.tcx()), adjustment);
382 ty::AutoAddEnv(..) => {
383 debug!("autoaddenv -- no autoref");
389 autoref: None, autoderefs }) => {
390 debug!("no autoref");
391 self.guarantee_autoderefs(expr, autoderefs);
397 autoref: Some(ref autoref),
399 self.guarantee_autoderefs(expr, autoderefs);
400 let mut mc = self.bccx.mc();
401 let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) {
403 Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc")
405 debug!("after autoderef, cmt={}", cmt.repr(self.tcx()));
408 ty::AutoPtr(r, m) => {
409 self.guarantee_valid(expr.id,
416 ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => {
417 let cmt_index = mc.cat_index(expr, cmt, autoderefs+1);
418 self.guarantee_valid(expr.id,
425 ty::AutoBorrowFn(r) => {
426 let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0);
427 self.guarantee_valid(expr.id,
434 ty::AutoBorrowObj(r, m) => {
435 let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0);
436 self.guarantee_valid(expr.id,
443 ty::AutoUnsafe(_) => {}
447 ty::AutoObject(..) => {
448 // FIXME: Handle ~Trait to &Trait casts here?
453 fn guarantee_captures(&mut self,
454 closure_expr: &ast::Expr) {
455 for captured_var in self.bccx.capture_map.get(&closure_expr.id).iter() {
456 match captured_var.mode {
457 moves::CapCopy | moves::CapMove => { continue; }
461 let var_id = ast_util::def_id_of_def(captured_var.def).node;
462 let var_cmt = self.bccx.cat_captured_var(closure_expr.id,
466 // Lookup the kind of borrow the callee requires
467 let upvar_id = ty::UpvarId { var_id: var_id,
468 closure_expr_id: closure_expr.id };
469 let upvar_borrow = self.tcx().upvar_borrow_map.borrow()
470 .get_copy(&upvar_id);
472 self.guarantee_valid_kind(closure_expr.id,
477 ClosureCapture(captured_var.span));
481 pub fn guarantee_valid(&mut self,
482 borrow_id: ast::NodeId,
485 req_mutbl: ast::Mutability,
486 loan_region: ty::Region,
488 self.guarantee_valid_kind(borrow_id,
491 ty::BorrowKind::from_mutbl(req_mutbl),
496 fn guarantee_valid_kind(&mut self,
497 borrow_id: ast::NodeId,
500 req_kind: ty::BorrowKind,
501 loan_region: ty::Region,
504 * Guarantees that `addr_of(cmt)` will be valid for the duration of
505 * `static_scope_r`, or reports an error. This may entail taking
506 * out loans, which will be added to the `req_loan_map`. This can
507 * also entail "rooting" GC'd pointers, which means ensuring
508 * dynamically that they are not freed.
511 debug!("guarantee_valid(borrow_id={:?}, cmt={}, \
512 req_mutbl={:?}, loan_region={:?})",
514 cmt.repr(self.tcx()),
518 // a loan for the empty region can never be dereferenced, so
520 if loan_region == ty::ReEmpty {
524 let root_ub = { *self.repeating_ids.last().unwrap() }; // FIXME(#5074)
526 // Check that the lifetime of the borrow does not exceed
527 // the lifetime of the data being borrowed.
528 if lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub,
529 borrow_span, cause, cmt, loan_region,
531 return; // reported an error, no sense in reporting more.
534 // Check that we don't allow mutable borrows of non-mutable data.
535 if check_mutability(self.bccx, borrow_span, cause,
536 cmt, req_kind).is_err() {
537 return; // reported an error, no sense in reporting more.
540 // Check that we don't allow mutable borrows of aliasable data.
541 if check_aliasability(self.bccx, borrow_span, cause,
542 cmt, req_kind).is_err() {
543 return; // reported an error, no sense in reporting more.
546 // Compute the restrictions that are required to enforce the
548 let restr = restrictions::compute_restrictions(
549 self.bccx, borrow_span, cause,
550 cmt, loan_region, self.restriction_set(req_kind));
552 // Create the loan record (if needed).
553 let loan = match restr {
554 restrictions::Safe => {
555 // No restrictions---no loan record necessary
559 restrictions::SafeIf(loan_path, restrictions) => {
560 let loan_scope = match loan_region {
561 ty::ReScope(id) => id,
562 ty::ReFree(ref fr) => fr.scope_id,
565 // If we get here, an error must have been
567 // `lifetime::guarantee_lifetime()`, because
568 // the only legal ways to have a borrow with a
569 // static lifetime should not require
570 // restrictions. To avoid reporting derived
571 // errors, we just return here without adding
577 ty::ReLateBound(..) |
578 ty::ReEarlyBound(..) |
580 self.tcx().sess.span_bug(
582 format!("invalid borrow lifetime: {:?}", loan_region));
585 debug!("loan_scope = {:?}", loan_scope);
587 let gen_scope = self.compute_gen_scope(borrow_id, loan_scope);
588 debug!("gen_scope = {:?}", gen_scope);
590 let kill_scope = self.compute_kill_scope(loan_scope, loan_path);
591 debug!("kill_scope = {:?}", kill_scope);
593 if req_kind == ty::MutBorrow {
594 self.mark_loan_path_as_mutated(loan_path);
598 index: self.all_loans.len(),
599 loan_path: loan_path,
602 gen_scope: gen_scope,
603 kill_scope: kill_scope,
605 restrictions: restrictions,
611 debug!("guarantee_valid(borrow_id={:?}), loan={}",
612 borrow_id, loan.repr(self.tcx()));
614 // let loan_path = loan.loan_path;
615 // let loan_gen_scope = loan.gen_scope;
616 // let loan_kill_scope = loan.kill_scope;
617 self.all_loans.push(loan);
619 // if loan_gen_scope != borrow_id {
620 // FIXME(#6268) Nested method calls
622 // Typically, the scope of the loan includes the point at
623 // which the loan is originated. This
624 // This is a subtle case. See the test case
625 // <compile-fail/borrowck-bad-nested-calls-free.rs>
626 // to see what we are guarding against.
628 //let restr = restrictions::compute_restrictions(
629 // self.bccx, borrow_span, cmt, RESTR_EMPTY);
631 // let all_loans = &mut *self.all_loans; // FIXME(#5074)
633 // index: all_loans.len(),
634 // loan_path: loan_path,
636 // mutbl: ConstMutability,
637 // gen_scope: borrow_id,
638 // kill_scope: kill_scope,
639 // span: borrow_span,
640 // restrictions: restrictions
644 fn check_mutability(bccx: &BorrowckCtxt,
648 req_kind: ty::BorrowKind)
650 //! Implements the M-* rules in doc.rs.
653 ty::UniqueImmBorrow | ty::ImmBorrow => {
655 // I am intentionally leaving this here to help
656 // refactoring if, in the future, we should add new
657 // kinds of mutability.
658 mc::McImmutable | mc::McDeclared | mc::McInherited => {
659 // both imm and mut data can be lent as imm;
660 // for mutable data, this is a freeze
667 // Only mutable data can be lent as mutable.
668 if !cmt.mutbl.is_mutable() {
669 Err(bccx.report(BckError { span: borrow_span,
680 fn check_aliasability(bccx: &BorrowckCtxt,
682 loan_cause: LoanCause,
684 req_kind: ty::BorrowKind)
686 //! Implements the A-* rules in doc.rs.
688 match (cmt.freely_aliasable(bccx.tcx), req_kind) {
690 /* Uniquely accessible path -- OK for `&` and `&mut` */
693 (Some(mc::AliasableStatic(safety)), ty::ImmBorrow) => {
694 // Borrow of an immutable static item:
696 mc::InteriorUnsafe => {
697 // If the static item contains an Unsafe<T>, it has interior mutability.
698 // In such cases, we cannot permit it to be borrowed, because the
699 // static item resides in immutable memory and mutating it would
701 bccx.tcx.sess.span_err(borrow_span,
702 format!("borrow of immutable static items with \
703 unsafe interior is not allowed"));
706 mc::InteriorSafe => {
707 // Immutable static can be borrowed, no problem.
712 (Some(mc::AliasableStaticMut(..)), _) => {
713 // Even touching a static mut is considered unsafe. We assume the
714 // user knows what they're doing in these cases.
717 (Some(alias_cause), ty::UniqueImmBorrow) |
718 (Some(alias_cause), ty::MutBorrow) => {
719 bccx.report_aliasability_violation(
721 BorrowViolation(loan_cause),
732 fn restriction_set(&self, req_kind: ty::BorrowKind) -> RestrictionSet {
734 // If borrowing data as immutable, no mutation allowed:
735 ty::ImmBorrow => RESTR_MUTATE,
737 // If borrowing data as mutable, no mutation nor other
739 ty::MutBorrow => RESTR_MUTATE | RESTR_FREEZE,
741 // If borrowing data as unique imm, no mutation nor other
743 ty::UniqueImmBorrow => RESTR_MUTATE | RESTR_FREEZE,
747 pub fn mark_loan_path_as_mutated(&self, loan_path: @LoanPath) {
748 //! For mutable loans of content whose mutability derives
749 //! from a local variable, mark the mutability decl as necessary.
753 self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
755 LpExtend(base, mc::McInherited, _) => {
756 self.mark_loan_path_as_mutated(base);
758 LpExtend(_, mc::McDeclared, _) |
759 LpExtend(_, mc::McImmutable, _) => {
765 pub fn compute_gen_scope(&self,
766 borrow_id: ast::NodeId,
767 loan_scope: ast::NodeId)
769 //! Determine when to introduce the loan. Typically the loan
770 //! is introduced at the point of the borrow, but in some cases,
771 //! notably method arguments, the loan may be introduced only
772 //! later, once it comes into scope.
774 if self.bccx.tcx.region_maps.is_subscope_of(borrow_id, loan_scope) {
781 pub fn compute_kill_scope(&self, loan_scope: ast::NodeId, lp: @LoanPath)
783 //! Determine when the loan restrictions go out of scope.
784 //! This is either when the lifetime expires or when the
785 //! local variable which roots the loan-path goes out of scope,
786 //! whichever happens faster.
788 //! It may seem surprising that we might have a loan region
789 //! larger than the variable which roots the loan-path; this can
790 //! come about when variables of `&mut` type are re-borrowed,
791 //! as in this example:
793 //! fn counter<'a>(v: &'a mut Foo) -> &'a mut uint {
797 //! In this case, the reference (`'a`) outlives the
798 //! variable `v` that hosts it. Note that this doesn't come up
799 //! with immutable `&` pointers, because borrows of such pointers
800 //! do not require restrictions and hence do not cause a loan.
802 let rm = &self.bccx.tcx.region_maps;
803 let lexical_scope = rm.var_scope(lp.node_id());
804 if rm.is_subscope_of(lexical_scope, loan_scope) {
807 assert!(self.bccx.tcx.region_maps.is_subscope_of(loan_scope, lexical_scope));
812 fn gather_fn_arg_patterns(&mut self,
816 * Walks the patterns for fn arguments, checking that they
817 * do not attempt illegal moves or create refs that outlive
818 * the arguments themselves. Just a shallow wrapper around
822 let mut mc = self.bccx.mc();
823 for arg in decl.inputs.iter() {
824 let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
826 let arg_cmt = mc.cat_rvalue(
829 ty::ReScope(body.id), // Args live only as long as the fn body.
832 self.gather_pat(arg_cmt, arg.pat, None);
836 fn gather_pat(&mut self,
839 arm_match_ids: Option<(ast::NodeId, ast::NodeId)>) {
841 * Walks patterns, examining the bindings to determine if they
842 * cause borrows (`ref` bindings, vector patterns) or
843 * moves (non-`ref` bindings with linear type).
846 self.bccx.cat_pattern(discr_cmt, root_pat, |cmt, pat| {
848 ast::PatIdent(bm, _, _) if self.pat_is_binding(pat) => {
849 // Each match binding is effectively an assignment.
850 let tcx = self.bccx.tcx;
851 pat_util::pat_bindings(tcx.def_map, pat, |_, id, span, _| {
852 gather_moves::gather_assignment(self.bccx,
861 ast::BindByRef(mutbl) => {
862 // ref x or ref x @ p --- creates a ptr which must
863 // remain valid for the scope of the match
865 // find the region of the resulting pointer (note that
866 // the type of such a pattern will *always* be a
869 ty_region(self.tcx(), pat.span,
870 ty::node_id_to_type(self.tcx(), pat.id));
872 // if the scope of the region ptr turns out to be
873 // specific to this arm, wrap the categorization
874 // with a cat_discr() node. There is a detailed
875 // discussion of the function of this node in
877 let cmt_discr = match arm_match_ids {
879 Some((arm_id, match_id)) => {
880 let arm_scope = ty::ReScope(arm_id);
881 if self.bccx.is_subregion_of(scope_r, arm_scope) {
882 self.bccx.cat_discr(cmt, match_id)
888 self.guarantee_valid(pat.id,
895 ast::BindByValue(_) => {
896 // No borrows here, but there may be moves
897 if self.bccx.is_move(pat.id) {
898 gather_moves::gather_move_from_pat(
899 self.bccx, &self.move_data, pat, cmt);
905 ast::PatVec(_, Some(slice_pat), _) => {
906 // The `slice_pat` here creates a slice into the
907 // original vector. This is effectively a borrow of
908 // the elements of the vector being matched.
910 let (slice_cmt, slice_borrow_kind, slice_r) = {
911 match self.bccx.mc().cat_slice_pattern(cmt, slice_pat) {
914 self.tcx().sess.span_bug(slice_pat.span,
920 // Note: We declare here that the borrow occurs upon
921 // entering the `[...]` pattern. This implies that
922 // something like `[a, ..b]` where `a` is a move is
923 // illegal, because the borrow is already in effect.
924 // In fact such a move would be safe-ish, but it
925 // effectively *requires* that we use the nulling
926 // out semantics to indicate when a value has been
927 // moved, which we are trying to move away from.
928 // Otherwise, how can we indicate that the first
929 // element in the vector has been moved?
930 // Eventually, we could perhaps modify this rule to
931 // permit `[..a, b]` where `b` is a move, because in
932 // that case we can adjust the length of the
933 // original vec accordingly, but we'd have to make
934 // trans do the right thing, and it would only work
935 // for `~` vectors. It seems simpler to just require
936 // that people call `vec.pop()` or `vec.unshift()`.
937 self.guarantee_valid(pat.id, pat.span,
938 slice_cmt, slice_borrow_kind, slice_r,
947 pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool {
948 pat_util::pat_is_binding(self.bccx.tcx.def_map, pat)