Skip to content

Commit 729b07f

Browse files
committed
Modify borrow checker to visit irrefutable patterns that appear in
let and function arguments; modify type checker to store type information for all patterns and handles some missing cases.
1 parent 2d3262c commit 729b07f

File tree

12 files changed

+249
-151
lines changed

12 files changed

+249
-151
lines changed

src/librustc/middle/borrowck/check_loans.rs

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ pub fn check_loans(bccx: @BorrowckCtxt,
6565

6666
enum MoveError {
6767
MoveOk,
68-
MoveWhileBorrowed(/*move*/@LoanPath, /*loan*/@LoanPath, /*loan*/span)
68+
MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
6969
}
7070

7171
impl<'self> CheckLoanCtxt<'self> {
@@ -348,7 +348,7 @@ impl<'self> CheckLoanCtxt<'self> {
348348
cmt = b;
349349
}
350350

351-
mc::cat_rvalue |
351+
mc::cat_rvalue(*) |
352352
mc::cat_static_item |
353353
mc::cat_implicit_self |
354354
mc::cat_copied_upvar(*) |
@@ -547,45 +547,50 @@ impl<'self> CheckLoanCtxt<'self> {
547547
self.bccx.loan_path_to_str(loan_path)));
548548
}
549549

550-
pub fn check_move_out_from_expr(&self, ex: @ast::expr) {
551-
match ex.node {
552-
ast::expr_paren(*) => {
553-
/* In the case of an expr_paren(), the expression inside
554-
* the parens will also be marked as being moved. Ignore
555-
* the parents then so as not to report duplicate errors. */
550+
fn check_move_out_from_expr(&self, expr: @ast::expr) {
551+
match expr.node {
552+
ast::expr_fn_block(*) => {
553+
// moves due to capture clauses are checked
554+
// in `check_loans_in_fn`, so that we can
555+
// give a better error message
556556
}
557557
_ => {
558-
let cmt = self.bccx.cat_expr(ex);
559-
match self.analyze_move_out_from_cmt(cmt) {
560-
MoveOk => {}
561-
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
562-
self.bccx.span_err(
563-
cmt.span,
564-
fmt!("cannot move out of `%s` \
565-
because it is borrowed",
566-
self.bccx.loan_path_to_str(move_path)));
567-
self.bccx.span_note(
568-
loan_span,
569-
fmt!("borrow of `%s` occurs here",
570-
self.bccx.loan_path_to_str(loan_path)));
571-
}
558+
self.check_move_out_from_id(expr.id, expr.span)
559+
}
560+
}
561+
}
562+
563+
fn check_move_out_from_id(&self, id: ast::node_id, span: span) {
564+
for self.move_data.each_path_moved_by(id) |_, move_path| {
565+
match self.analyze_move_out_from(id, move_path) {
566+
MoveOk => {}
567+
MoveWhileBorrowed(loan_path, loan_span) => {
568+
self.bccx.span_err(
569+
span,
570+
fmt!("cannot move out of `%s` \
571+
because it is borrowed",
572+
self.bccx.loan_path_to_str(move_path)));
573+
self.bccx.span_note(
574+
loan_span,
575+
fmt!("borrow of `%s` occurs here",
576+
self.bccx.loan_path_to_str(loan_path)));
572577
}
573578
}
574579
}
575580
}
576581

577-
pub fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
578-
debug!("analyze_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
582+
pub fn analyze_move_out_from(&self,
583+
expr_id: ast::node_id,
584+
move_path: @LoanPath) -> MoveError {
585+
debug!("analyze_move_out_from(expr_id=%?, move_path=%s)",
586+
expr_id, move_path.repr(self.tcx()));
579587

580588
// FIXME(#4384) inadequare if/when we permit `move a.b`
581589

582590
// check for a conflicting loan:
583-
let r = opt_loan_path(cmt);
584-
for r.iter().advance |&lp| {
585-
for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
586-
// Any restriction prevents moves.
587-
return MoveWhileBorrowed(lp, loan.loan_path, loan.span);
588-
}
591+
for self.each_in_scope_restriction(expr_id, move_path) |loan, _| {
592+
// Any restriction prevents moves.
593+
return MoveWhileBorrowed(loan.loan_path, loan.span);
589594
}
590595

591596
MoveOk
@@ -652,13 +657,11 @@ fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
652657
closure_id: ast::node_id,
653658
cap_var: &moves::CaptureVar) {
654659
let var_id = ast_util::def_id_of_def(cap_var.def).node;
655-
let ty = ty::node_id_to_type(this.tcx(), var_id);
656-
let cmt = this.bccx.cat_def(closure_id, cap_var.span,
657-
ty, cap_var.def);
658-
let move_err = this.analyze_move_out_from_cmt(cmt);
660+
let move_path = @LpVar(var_id);
661+
let move_err = this.analyze_move_out_from(closure_id, move_path);
659662
match move_err {
660663
MoveOk => {}
661-
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
664+
MoveWhileBorrowed(loan_path, loan_span) => {
662665
this.bccx.span_err(
663666
cap_var.span,
664667
fmt!("cannot move `%s` into closure \
@@ -689,10 +692,7 @@ fn check_loans_in_expr<'a>(expr: @ast::expr,
689692
expr.repr(this.tcx()));
690693

691694
this.check_for_conflicting_loans(expr.id);
692-
693-
if this.bccx.moves_map.contains(&expr.id) {
694-
this.check_move_out_from_expr(expr);
695-
}
695+
this.check_move_out_from_expr(expr);
696696

697697
match expr.node {
698698
ast::expr_self |
@@ -742,18 +742,7 @@ fn check_loans_in_pat<'a>(pat: @ast::pat,
742742
visit::vt<@mut CheckLoanCtxt<'a>>))
743743
{
744744
this.check_for_conflicting_loans(pat.id);
745-
746-
// Note: moves out of pattern bindings are not checked by
747-
// the borrow checker, at least not directly. What happens
748-
// is that if there are any moved bindings, the discriminant
749-
// will be considered a move, and this will be checked as
750-
// normal. Then, in `middle::check_match`, we will check
751-
// that no move occurs in a binding that is underneath an
752-
// `@` or `&`. Together these give the same guarantees as
753-
// `check_move_out_from_expr()` without requiring us to
754-
// rewalk the patterns and rebuild the pattern
755-
// categorizations.
756-
745+
this.check_move_out_from_id(pat.id, pat.span);
757746
visit::visit_pat(pat, (this, vt));
758747
}
759748

src/librustc/middle/borrowck/gather_loans/lifetime.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl GuaranteeLifetimeContext {
6767
//! Main routine. Walks down `cmt` until we find the "guarantor".
6868
6969
match cmt.cat {
70-
mc::cat_rvalue |
70+
mc::cat_rvalue(*) |
7171
mc::cat_implicit_self |
7272
mc::cat_copied_upvar(*) | // L-Local
7373
mc::cat_local(*) | // L-Local
@@ -179,7 +179,7 @@ impl GuaranteeLifetimeContext {
179179
//! lvalue.
180180
181181
cmt.mutbl.is_immutable() || match cmt.guarantor().cat {
182-
mc::cat_rvalue => true,
182+
mc::cat_rvalue(*) => true,
183183
_ => false
184184
}
185185
}
@@ -299,7 +299,7 @@ impl GuaranteeLifetimeContext {
299299
mc::cat_arg(id) => {
300300
self.bccx.moved_variables_set.contains(&id)
301301
}
302-
mc::cat_rvalue |
302+
mc::cat_rvalue(*) |
303303
mc::cat_static_item |
304304
mc::cat_implicit_self |
305305
mc::cat_copied_upvar(*) |
@@ -325,8 +325,8 @@ impl GuaranteeLifetimeContext {
325325
// See the SCOPE(LV) function in doc.rs
326326

327327
match cmt.cat {
328-
mc::cat_rvalue => {
329-
ty::re_scope(self.bccx.tcx.region_maps.cleanup_scope(cmt.id))
328+
mc::cat_rvalue(cleanup_scope_id) => {
329+
ty::re_scope(cleanup_scope_id)
330330
}
331331
mc::cat_implicit_self |
332332
mc::cat_copied_upvar(_) => {

src/librustc/middle/borrowck/gather_loans/mod.rs

Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct GatherLoanCtxt {
7373
}
7474

7575
pub fn gather_loans(bccx: @BorrowckCtxt,
76+
decl: &ast::fn_decl,
7677
body: &ast::blk)
7778
-> (id_range, @mut ~[Loan], @mut move_data::MoveData) {
7879
let glcx = @mut GatherLoanCtxt {
@@ -83,6 +84,7 @@ pub fn gather_loans(bccx: @BorrowckCtxt,
8384
repeating_ids: ~[body.node.id],
8485
move_data: @mut MoveData::new()
8586
};
87+
glcx.gather_fn_arg_patterns(decl, body);
8688
let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr,
8789
visit_block: gather_loans_in_block,
8890
visit_fn: gather_loans_in_fn,
@@ -124,6 +126,7 @@ fn gather_loans_in_fn(fk: &visit::fn_kind,
124126
this.push_repeating_id(body.node.id);
125127
visit::visit_fn(fk, decl, body, sp, id, (this, v));
126128
this.pop_repeating_id(body.node.id);
129+
this.gather_fn_arg_patterns(decl, body);
127130
}
128131
}
129132
}
@@ -138,26 +141,33 @@ fn gather_loans_in_block(blk: &ast::blk,
138141
fn gather_loans_in_local(local: @ast::local,
139142
(this, vt): (@mut GatherLoanCtxt,
140143
visit::vt<@mut GatherLoanCtxt>)) {
141-
if local.node.init.is_none() {
142-
// Variable declarations without initializers are considered "moves":
143-
let tcx = this.bccx.tcx;
144-
do pat_util::pat_bindings(tcx.def_map, local.node.pat) |_, id, span, _| {
145-
gather_moves::gather_decl(this.bccx,
146-
this.move_data,
147-
id,
148-
span,
149-
id);
144+
match local.node.init {
145+
None => {
146+
// Variable declarations without initializers are considered "moves":
147+
let tcx = this.bccx.tcx;
148+
do pat_util::pat_bindings(tcx.def_map, local.node.pat)
149+
|_, id, span, _| {
150+
gather_moves::gather_decl(this.bccx,
151+
this.move_data,
152+
id,
153+
span,
154+
id);
155+
}
150156
}
151-
} else {
152-
// Variable declarations with initializers are considered "assigns":
153-
let tcx = this.bccx.tcx;
154-
do pat_util::pat_bindings(tcx.def_map, local.node.pat) |_, id, span, _| {
155-
gather_moves::gather_assignment(this.bccx,
156-
this.move_data,
157-
id,
158-
span,
159-
@LpVar(id),
160-
id);
157+
Some(init) => {
158+
// Variable declarations with initializers are considered "assigns":
159+
let tcx = this.bccx.tcx;
160+
do pat_util::pat_bindings(tcx.def_map, local.node.pat)
161+
|_, id, span, _| {
162+
gather_moves::gather_assignment(this.bccx,
163+
this.move_data,
164+
id,
165+
span,
166+
@LpVar(id),
167+
id);
168+
}
169+
let init_cmt = this.bccx.cat_expr(init);
170+
this.gather_pat(init_cmt, local.node.pat, None);
161171
}
162172
}
163173

@@ -230,7 +240,7 @@ fn gather_loans_in_expr(ex: @ast::expr,
230240
let cmt = this.bccx.cat_expr(ex_v);
231241
for arms.iter().advance |arm| {
232242
for arm.pats.iter().advance |pat| {
233-
this.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
243+
this.gather_pat(cmt, *pat, Some((arm.body.node.id, ex.id)));
234244
}
235245
}
236246
visit::visit_expr(ex, (this, vt));
@@ -596,11 +606,40 @@ impl GatherLoanCtxt {
596606
}
597607
}
598608

599-
pub fn gather_pat(&mut self,
600-
discr_cmt: mc::cmt,
601-
root_pat: @ast::pat,
602-
arm_body_id: ast::node_id,
603-
match_id: ast::node_id) {
609+
fn gather_fn_arg_patterns(&mut self,
610+
decl: &ast::fn_decl,
611+
body: &ast::blk) {
612+
/*!
613+
* Walks the patterns for fn arguments, checking that they
614+
* do not attempt illegal moves or create refs that outlive
615+
* the arguments themselves. Just a shallow wrapper around
616+
* `gather_pat()`.
617+
*/
618+
619+
let mc_ctxt = self.bccx.mc_ctxt();
620+
for decl.inputs.each |arg| {
621+
let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
622+
623+
let arg_cmt = mc_ctxt.cat_rvalue(
624+
arg.id,
625+
arg.pat.span,
626+
body.node.id, // Arguments live only as long as the fn body.
627+
arg_ty);
628+
629+
self.gather_pat(arg_cmt, arg.pat, None);
630+
}
631+
}
632+
633+
fn gather_pat(&mut self,
634+
discr_cmt: mc::cmt,
635+
root_pat: @ast::pat,
636+
arm_match_ids: Option<(ast::node_id, ast::node_id)>) {
637+
/*!
638+
* Walks patterns, examining the bindings to determine if they
639+
* cause borrows (`ref` bindings, vector patterns) or
640+
* moves (non-`ref` bindings with linear type).
641+
*/
642+
604643
do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
605644
match pat.node {
606645
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
@@ -621,15 +660,19 @@ impl GatherLoanCtxt {
621660
// with a cat_discr() node. There is a detailed
622661
// discussion of the function of this node in
623662
// `lifetime.rs`:
624-
let arm_scope = ty::re_scope(arm_body_id);
625-
if self.bccx.is_subregion_of(scope_r, arm_scope) {
626-
let cmt_discr = self.bccx.cat_discr(cmt, match_id);
627-
self.guarantee_valid(pat.id, pat.span,
628-
cmt_discr, mutbl, scope_r);
629-
} else {
630-
self.guarantee_valid(pat.id, pat.span,
631-
cmt, mutbl, scope_r);
632-
}
663+
let cmt_discr = match arm_match_ids {
664+
None => cmt,
665+
Some((arm_id, match_id)) => {
666+
let arm_scope = ty::re_scope(arm_id);
667+
if self.bccx.is_subregion_of(scope_r, arm_scope) {
668+
self.bccx.cat_discr(cmt, match_id)
669+
} else {
670+
cmt
671+
}
672+
}
673+
};
674+
self.guarantee_valid(pat.id, pat.span,
675+
cmt_discr, mutbl, scope_r);
633676
}
634677
ast::bind_infer => {
635678
// No borrows here, but there may be moves
@@ -652,6 +695,24 @@ impl GatherLoanCtxt {
652695
self.vec_slice_info(slice_pat, slice_ty);
653696
let mcx = self.bccx.mc_ctxt();
654697
let cmt_index = mcx.cat_index(slice_pat, cmt, 0);
698+
699+
// Note: We declare here that the borrow occurs upon
700+
// entering the `[...]` pattern. This implies that
701+
// something like `[a, ..b]` where `a` is a move is
702+
// illegal, because the borrow is already in effect.
703+
// In fact such a move would be safe-ish, but it
704+
// effectively *requires* that we use the nulling
705+
// out semantics to indicate when a value has been
706+
// moved, which we are trying to move away from.
707+
// Otherwise, how can we indicate that the first
708+
// element in the vector has been moved?
709+
// Eventually, we could perhaps modify this rule to
710+
// permit `[..a, b]` where `b` is a move, because in
711+
// that case we can adjust the length of the
712+
// original vec accordingly, but we'd have to make
713+
// trans do the right thing, and it would only work
714+
// for `~` vectors. It seems simpler to just require
715+
// that people call `vec.pop()` or `vec.unshift()`.
655716
self.guarantee_valid(pat.id, pat.span,
656717
cmt_index, slice_mutbl, slice_r);
657718
}

src/librustc/middle/borrowck/gather_loans/restrictions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl RestrictionsContext {
6464
}
6565

6666
match cmt.cat {
67-
mc::cat_rvalue => {
67+
mc::cat_rvalue(*) => {
6868
// Effectively, rvalues are stored into a
6969
// non-aliasable temporary on the stack. Since they
7070
// are inherently non-aliasable, they can only be

0 commit comments

Comments
 (0)