diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 235775c58f5..fed1fb9c609 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -20,15 +20,10 @@ use middle::borrowck::*; use euv = middle::expr_use_visitor; -use middle::freevars; use mc = middle::mem_categorization; use middle::ty; -use middle::typeck::MethodCall; use syntax::ast; -use syntax::ast_util; use syntax::codemap::Span; -use syntax::visit::Visitor; -use syntax::visit; use util::ppaux::Repr; use std::rc::Rc; @@ -40,35 +35,104 @@ struct CheckLoanCtxt<'a> { all_loans: &'a [Loan], } -impl<'a> Visitor<()> for CheckLoanCtxt<'a> { +impl<'a> euv::Delegate for CheckLoanCtxt<'a> { + fn consume(&mut self, + consume_id: ast::NodeId, + consume_span: Span, + cmt: mc::cmt, + mode: euv::ConsumeMode) { + debug!("consume(consume_id={}, cmt={}, mode={})", + consume_id, cmt.repr(self.tcx()), mode); - fn visit_expr(&mut self, ex: &ast::Expr, _: ()) { - check_loans_in_expr(self, ex); - } - fn visit_local(&mut self, l: &ast::Local, _: ()) { - check_loans_in_local(self, l); - } - fn visit_block(&mut self, b: &ast::Block, _: ()) { - check_loans_in_block(self, b); - } - fn visit_pat(&mut self, p: &ast::Pat, _: ()) { - check_loans_in_pat(self, p); - } - fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl, - _b: &ast::Block, _s: Span, _n: ast::NodeId, _: ()) { - // Don't process nested items or closures here, - // the outer loop will take care of it. - return; + self.consume_common(consume_id, consume_span, cmt, mode); } - // FIXME(#10894) should continue recursing - fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {} + fn consume_pat(&mut self, + consume_pat: &ast::Pat, + cmt: mc::cmt, + mode: euv::ConsumeMode) { + debug!("consume_pat(consume_pat={}, cmt={}, mode={})", + consume_pat.repr(self.tcx()), + cmt.repr(self.tcx()), + mode); + + self.consume_common(consume_pat.id, consume_pat.span, cmt, mode); + } + + fn borrow(&mut self, + borrow_id: ast::NodeId, + borrow_span: Span, + cmt: mc::cmt, + loan_region: ty::Region, + bk: ty::BorrowKind, + loan_cause: euv::LoanCause) + { + debug!("borrow(borrow_id={}, cmt={}, loan_region={}, \ + bk={}, loan_cause={:?})", + borrow_id, cmt.repr(self.tcx()), loan_region, + bk, loan_cause); + + match opt_loan_path(&cmt) { + Some(lp) => { + let moved_value_use_kind = match loan_cause { + euv::ClosureCapture(_) => MovedInCapture, + _ => MovedInUse, + }; + self.check_if_path_is_moved(borrow_id, borrow_span, moved_value_use_kind, &lp); + } + None => { } + } + + self.check_for_conflicting_loans(borrow_id); + } + + fn mutate(&mut self, + assignment_id: ast::NodeId, + assignment_span: Span, + assignee_cmt: mc::cmt, + mode: euv::MutateMode) + { + debug!("mutate(assignment_id={}, assignee_cmt={})", + assignment_id, assignee_cmt.repr(self.tcx())); + + match opt_loan_path(&assignee_cmt) { + Some(lp) => { + match mode { + euv::Init | euv::JustWrite => { + // In a case like `path = 1`, then path does not + // have to be *FULLY* initialized, but we still + // must be careful lest it contains derefs of + // pointers. + self.check_if_assigned_path_is_moved(assignee_cmt.id, + assignment_span, + MovedInUse, + &lp); + } + euv::WriteAndRead => { + // In a case like `path += 1`, then path must be + // fully initialized, since we will read it before + // we write it. + self.check_if_path_is_moved(assignee_cmt.id, + assignment_span, + MovedInUse, + &lp); + } + } + } + None => { } + } + + self.check_assignment(assignment_id, assignment_span, assignee_cmt, mode); + } + + fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) { } } pub fn check_loans(bccx: &BorrowckCtxt, dfcx_loans: &LoanDataFlow, move_data: move_data::FlowedMoveData, all_loans: &[Loan], + decl: &ast::FnDecl, body: &ast::Block) { debug!("check_loans(body id={:?})", body.id); @@ -79,7 +143,10 @@ pub fn check_loans(bccx: &BorrowckCtxt, all_loans: all_loans, }; - clcx.visit_block(body, ()); + { + let mut euv = euv::ExprUseVisitor::new(&mut clcx, bccx.tcx); + euv.walk_fn(decl, body); + } } #[deriving(PartialEq)] @@ -355,18 +422,130 @@ impl<'a> CheckLoanCtxt<'a> { true } - pub fn is_local_variable(&self, cmt: mc::cmt) -> bool { + pub fn is_local_variable_or_arg(&self, cmt: mc::cmt) -> bool { match cmt.cat { - mc::cat_local(_) => true, + mc::cat_local(_) | mc::cat_arg(_) => true, _ => false } } - pub fn check_if_path_is_moved(&self, - id: ast::NodeId, - span: Span, - use_kind: MovedValueUseKind, - lp: &Rc) { + fn consume_common(&self, + id: ast::NodeId, + span: Span, + cmt: mc::cmt, + mode: euv::ConsumeMode) { + match opt_loan_path(&cmt) { + Some(lp) => { + let moved_value_use_kind = match mode { + euv::Copy => { + // FIXME(#12624) -- If we are copying the value, + // we don't care if it's borrowed. + MovedInUse + } + euv::Move(_) => { + match self.move_data.kind_of_move_of_path(id, &lp) { + None => { + // Sometimes moves don't have a move kind; + // this either means that the original move + // was from something illegal to move, + // or was moved from referent of an unsafe + // pointer or something like that. + MovedInUse + } + Some(move_kind) => { + self.check_for_move_of_borrowed_path(id, span, + &lp, move_kind); + if move_kind == move_data::Captured { + MovedInCapture + } else { + MovedInUse + } + } + } + } + }; + + self.check_if_path_is_moved(id, span, moved_value_use_kind, &lp); + } + None => { } + } + } + + fn check_for_move_of_borrowed_path(&self, + id: ast::NodeId, + span: Span, + move_path: &Rc, + move_kind: move_data::MoveKind) { + match self.analyze_move_out_from(id, &**move_path) { + MoveOk => { } + MoveWhileBorrowed(loan_path, loan_span) => { + let err_message = match move_kind { + move_data::Captured => + format!("cannot move `{}` into closure because it is borrowed", + self.bccx.loan_path_to_str(&**move_path).as_slice()), + move_data::Declared | + move_data::MoveExpr | + move_data::MovePat => + format!("cannot move out of `{}` because it is borrowed", + self.bccx.loan_path_to_str(&**move_path).as_slice()) + }; + + self.bccx.span_err(span, err_message.as_slice()); + self.bccx.span_note( + loan_span, + format!("borrow of `{}` occurs here", + self.bccx.loan_path_to_str(&*loan_path).as_slice()) + .as_slice()); + } + } + } + + fn check_if_assigned_path_is_moved(&self, + id: ast::NodeId, + span: Span, + use_kind: MovedValueUseKind, + lp: &Rc) + { + /*! + * Reports an error if assigning to `lp` will use a + * moved/uninitialized value. Mainly this is concerned with + * detecting derefs of uninitialized pointers. + * + * For example: + * + * let a: int; + * a = 10; // ok, even though a is uninitialized + * + * struct Point { x: uint, y: uint } + * let p: Point; + * p.x = 22; // ok, even though `p` is uninitialized + * + * let p: ~Point; + * (*p).x = 22; // not ok, p is uninitialized, can't deref + */ + + match **lp { + LpVar(_) => { + // assigning to `x` does not require that `x` is initialized + } + LpExtend(ref lp_base, _, LpInterior(_)) => { + // assigning to `P.f` is ok if assigning to `P` is ok + self.check_if_assigned_path_is_moved(id, span, + use_kind, lp_base); + } + LpExtend(ref lp_base, _, LpDeref(_)) => { + // assigning to `(*P)` requires that `P` be initialized + self.check_if_path_is_moved(id, span, + use_kind, lp_base); + } + } + } + + fn check_if_path_is_moved(&self, + id: ast::NodeId, + span: Span, + use_kind: MovedValueUseKind, + lp: &Rc) { /*! * Reports an error if `expr` (which should be a path) * is using a moved/uninitialized value @@ -385,24 +564,23 @@ impl<'a> CheckLoanCtxt<'a> { }); } - pub fn check_assignment(&self, expr: &ast::Expr) { - // We don't use cat_expr() here because we don't want to treat - // auto-ref'd parameters in overloaded operators as rvalues. - let cmt = match self.bccx.tcx.adjustments.borrow().find(&expr.id) { - None => self.bccx.cat_expr_unadjusted(expr), - Some(adj) => self.bccx.cat_expr_autoderefd(expr, adj) - }; - - debug!("check_assignment(cmt={})", cmt.repr(self.tcx())); + fn check_assignment(&self, + assignment_id: ast::NodeId, + assignment_span: Span, + assignee_cmt: mc::cmt, + mode: euv::MutateMode) { + debug!("check_assignment(assignee_cmt={})", assignee_cmt.repr(self.tcx())); // Mutable values can be assigned, as long as they obey loans // and aliasing restrictions: - if cmt.mutbl.is_mutable() { - if check_for_aliasable_mutable_writes(self, expr, cmt.clone()) { - if check_for_assignment_to_restricted_or_frozen_location( - self, expr, cmt.clone()) { + if assignee_cmt.mutbl.is_mutable() { + if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) { + if mode != euv::Init && + check_for_assignment_to_restricted_or_frozen_location( + self, assignment_id, assignment_span, assignee_cmt.clone()) + { // Safe, but record for lint pass later: - mark_variable_as_used_mut(self, cmt); + mark_variable_as_used_mut(self, assignee_cmt); } } return; @@ -410,12 +588,12 @@ impl<'a> CheckLoanCtxt<'a> { // For immutable local variables, assignments are legal // if they cannot already have been assigned - if self.is_local_variable(cmt.clone()) { - assert!(cmt.mutbl.is_immutable()); // no "const" locals - let lp = opt_loan_path(&cmt).unwrap(); - self.move_data.each_assignment_of(expr.id, &lp, |assign| { + if self.is_local_variable_or_arg(assignee_cmt.clone()) { + assert!(assignee_cmt.mutbl.is_immutable()); // no "const" locals + let lp = opt_loan_path(&assignee_cmt).unwrap(); + self.move_data.each_assignment_of(assignment_id, &lp, |assign| { self.bccx.report_reassigned_immutable_variable( - expr.span, + assignment_span, &*lp, assign); false @@ -424,21 +602,21 @@ impl<'a> CheckLoanCtxt<'a> { } // Otherwise, just a plain error. - match opt_loan_path(&cmt) { + match opt_loan_path(&assignee_cmt) { Some(lp) => { self.bccx.span_err( - expr.span, + assignment_span, format!("cannot assign to {} {} `{}`", - cmt.mutbl.to_user_str(), - self.bccx.cmt_to_str(&*cmt), + assignee_cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(&*assignee_cmt), self.bccx.loan_path_to_str(&*lp)).as_slice()); } None => { self.bccx.span_err( - expr.span, + assignment_span, format!("cannot assign to {} {}", - cmt.mutbl.to_user_str(), - self.bccx.cmt_to_str(&*cmt)).as_slice()); + assignee_cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(&*assignee_cmt)).as_slice()); } } return; @@ -495,7 +673,7 @@ impl<'a> CheckLoanCtxt<'a> { } fn check_for_aliasable_mutable_writes(this: &CheckLoanCtxt, - expr: &ast::Expr, + span: Span, cmt: mc::cmt) -> bool { //! Safety checks related to writes to aliasable, mutable locations @@ -506,7 +684,7 @@ impl<'a> CheckLoanCtxt<'a> { mc::cat_deref(ref b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => { // Statically prohibit writes to `&mut` when aliasable - check_for_aliasability_violation(this, expr, b.clone()); + check_for_aliasability_violation(this, span, b.clone()); } _ => {} @@ -516,7 +694,7 @@ impl<'a> CheckLoanCtxt<'a> { } fn check_for_aliasability_violation(this: &CheckLoanCtxt, - expr: &ast::Expr, + span: Span, cmt: mc::cmt) -> bool { match cmt.freely_aliasable(this.tcx()) { @@ -528,7 +706,7 @@ impl<'a> CheckLoanCtxt<'a> { } Some(cause) => { this.bccx.report_aliasability_violation( - expr.span, + span, MutabilityViolation, cause); return false; @@ -538,13 +716,14 @@ impl<'a> CheckLoanCtxt<'a> { fn check_for_assignment_to_restricted_or_frozen_location( this: &CheckLoanCtxt, - expr: &ast::Expr, - cmt: mc::cmt) -> bool + assignment_id: ast::NodeId, + assignment_span: Span, + assignee_cmt: mc::cmt) -> bool { //! Check for assignments that violate the terms of an //! outstanding loan. - let loan_path = match opt_loan_path(&cmt) { + let loan_path = match opt_loan_path(&assignee_cmt) { Some(lp) => lp, None => { return true; /* no loan path, can't be any loans */ } }; @@ -579,11 +758,11 @@ impl<'a> CheckLoanCtxt<'a> { // `RESTR_MUTATE` restriction whenever the contents of an // owned pointer are borrowed, and hence while `v[*]` is not // restricted from being written, `v` is. - let cont = this.each_in_scope_restriction(expr.id, + let cont = this.each_in_scope_restriction(assignment_id, &*loan_path, |loan, restr| { if restr.set.intersects(RESTR_MUTATE) { - this.report_illegal_mutation(expr, &*loan_path, loan); + this.report_illegal_mutation(assignment_span, &*loan_path, loan); false } else { true @@ -656,9 +835,9 @@ impl<'a> CheckLoanCtxt<'a> { }; // Check for a non-const loan of `loan_path` - let cont = this.each_in_scope_loan(expr.id, |loan| { + let cont = this.each_in_scope_loan(assignment_id, |loan| { if loan.loan_path == loan_path { - this.report_illegal_mutation(expr, &*full_loan_path, loan); + this.report_illegal_mutation(assignment_span, &*full_loan_path, loan); false } else { true @@ -671,11 +850,11 @@ impl<'a> CheckLoanCtxt<'a> { } pub fn report_illegal_mutation(&self, - expr: &ast::Expr, + span: Span, loan_path: &LoanPath, loan: &Loan) { self.bccx.span_err( - expr.span, + span, format!("cannot assign to `{}` because it is borrowed", self.bccx.loan_path_to_str(loan_path)).as_slice()); self.bccx.span_note( @@ -684,86 +863,6 @@ impl<'a> CheckLoanCtxt<'a> { self.bccx.loan_path_to_str(loan_path)).as_slice()); } - fn check_move_out_from_expr(&self, expr: &ast::Expr) { - match expr.node { - ast::ExprFnBlock(..) | ast::ExprProc(..) => { - // Moves due to captures are checked in - // check_captured_variables() because it allows - // us to give a more precise error message with - // a more precise span. - } - _ => { - self.check_move_out_from_id(expr.id, expr.span) - } - } - } - - fn check_move_out_from_id(&self, id: ast::NodeId, span: Span) { - self.move_data.each_path_moved_by(id, |_, move_path| { - match self.analyze_move_out_from(id, move_path) { - MoveOk => {} - MoveWhileBorrowed(loan_path, loan_span) => { - self.bccx.span_err( - span, - format!("cannot move out of `{}` \ - because it is borrowed", - self.bccx.loan_path_to_str( - move_path)).as_slice()); - self.bccx.span_note( - loan_span, - format!("borrow of `{}` occurs here", - self.bccx.loan_path_to_str( - &*loan_path)).as_slice()); - } - } - true - }); - } - - fn check_captured_variables(&self, - closure_id: ast::NodeId, - span: Span) { - let freevar_mode = freevars::get_capture_mode(self.tcx(), closure_id); - freevars::with_freevars(self.tcx(), closure_id, |freevars| { - for freevar in freevars.iter() { - let var_id = ast_util::def_id_of_def(freevar.def).node; - let var_path = Rc::new(LpVar(var_id)); - self.check_if_path_is_moved(closure_id, span, - MovedInCapture, &var_path); - match freevar_mode { - freevars::CaptureByRef => { } - freevars::CaptureByValue => { - check_by_move_capture(self, closure_id, freevar, &*var_path); - } - } - } - }); - return; - - fn check_by_move_capture(this: &CheckLoanCtxt, - closure_id: ast::NodeId, - freevar: &freevars::freevar_entry, - move_path: &LoanPath) { - let move_err = this.analyze_move_out_from(closure_id, move_path); - match move_err { - MoveOk => {} - MoveWhileBorrowed(loan_path, loan_span) => { - this.bccx.span_err( - freevar.span, - format!("cannot move `{}` into closure \ - because it is borrowed", - this.bccx.loan_path_to_str( - move_path)).as_slice()); - this.bccx.span_note( - loan_span, - format!("borrow of `{}` occurs here", - this.bccx.loan_path_to_str( - &*loan_path)).as_slice()); - } - } - } - } - pub fn analyze_move_out_from(&self, expr_id: ast::NodeId, move_path: &LoanPath) @@ -794,89 +893,5 @@ impl<'a> CheckLoanCtxt<'a> { } } } - - pub fn check_call(&self, - _expr: &ast::Expr, - _callee: Option<@ast::Expr>, - _callee_span: Span, - _args: &[@ast::Expr]) { - // NB: This call to check for conflicting loans is not truly - // necessary, because the callee_id never issues new loans. - // However, I added it for consistency and lest the system - // should change in the future. - // - // FIXME(#6268) nested method calls - // self.check_for_conflicting_loans(callee_id); - } } -fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>, - local: &ast::Local) { - visit::walk_local(this, local, ()); -} - -fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>, - expr: &ast::Expr) { - visit::walk_expr(this, expr, ()); - - debug!("check_loans_in_expr(expr={})", - expr.repr(this.tcx())); - - this.check_for_conflicting_loans(expr.id); - this.check_move_out_from_expr(expr); - - let method_map = this.bccx.tcx.method_map.borrow(); - match expr.node { - ast::ExprPath(..) => { - if !this.move_data.is_assignee(expr.id) { - let cmt = this.bccx.cat_expr_unadjusted(expr); - debug!("path cmt={}", cmt.repr(this.tcx())); - for lp in opt_loan_path(&cmt).iter() { - this.check_if_path_is_moved(expr.id, expr.span, MovedInUse, lp); - } - } - } - ast::ExprFnBlock(..) | ast::ExprProc(..) => { - this.check_captured_variables(expr.id, expr.span) - } - ast::ExprAssign(dest, _) | - ast::ExprAssignOp(_, dest, _) => { - this.check_assignment(dest); - } - ast::ExprCall(f, ref args) => { - this.check_call(expr, Some(f), f.span, args.as_slice()); - } - ast::ExprMethodCall(_, _, ref args) => { - this.check_call(expr, None, expr.span, args.as_slice()); - } - ast::ExprIndex(_, rval) | ast::ExprBinary(_, _, rval) - if method_map.contains_key(&MethodCall::expr(expr.id)) => { - this.check_call(expr, None, expr.span, [rval]); - } - ast::ExprUnary(_, _) | ast::ExprIndex(_, _) - if method_map.contains_key(&MethodCall::expr(expr.id)) => { - this.check_call(expr, None, expr.span, []); - } - ast::ExprInlineAsm(ref ia) => { - for &(_, out) in ia.outputs.iter() { - this.check_assignment(out); - } - } - _ => {} - } -} - -fn check_loans_in_pat<'a>(this: &mut CheckLoanCtxt<'a>, - pat: &ast::Pat) -{ - this.check_for_conflicting_loans(pat.id); - this.check_move_out_from_id(pat.id, pat.span); - visit::walk_pat(this, pat, ()); -} - -fn check_loans_in_block<'a>(this: &mut CheckLoanCtxt<'a>, - blk: &ast::Block) -{ - visit::walk_block(this, blk, ()); - this.check_for_conflicting_loans(blk.id); -} diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index 16fd03c1aad..6df47480977 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -45,10 +45,15 @@ pub fn gather_move_from_expr(bccx: &BorrowckCtxt, move_data: &MoveData, move_error_collector: &MoveErrorCollector, move_expr_id: ast::NodeId, - cmt: mc::cmt) { + cmt: mc::cmt, + move_reason: euv::MoveReason) { + let kind = match move_reason { + euv::DirectRefMove | euv::PatBindingMove => MoveExpr, + euv::CaptureMove => Captured + }; let move_info = GatherMoveInfo { id: move_expr_id, - kind: MoveExpr, + kind: kind, cmt: cmt, span_path_opt: None, }; diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index d34ce7f6a5f..003687e7b63 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -75,13 +75,13 @@ impl<'a> euv::Delegate for GatherLoanCtxt<'a> { consume_id, cmt.repr(self.tcx()), mode); match mode { - euv::Copy => { return; } - euv::Move => { } + euv::Move(move_reason) => { + gather_moves::gather_move_from_expr( + self.bccx, &self.move_data, &self.move_error_collector, + consume_id, cmt, move_reason); + } + euv::Copy => { } } - - gather_moves::gather_move_from_expr( - self.bccx, &self.move_data, &self.move_error_collector, - consume_id, cmt); } fn consume_pat(&mut self, @@ -95,7 +95,7 @@ impl<'a> euv::Delegate for GatherLoanCtxt<'a> { match mode { euv::Copy => { return; } - euv::Move => { } + euv::Move(_) => { } } gather_moves::gather_move_from_pat( diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index ce111d3ae29..0fbcf157dac 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -142,7 +142,7 @@ fn borrowck_fn(this: &mut BorrowckCtxt, body); check_loans::check_loans(this, &loan_dfcx, flowed_moves, - all_loans.as_slice(), body); + all_loans.as_slice(), decl, body); visit::walk_fn(this, fk, decl, body, sp, ()); } diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index 5a4ec36befc..f7c26292334 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -115,6 +115,7 @@ pub struct MovePath { pub next_sibling: MovePathIndex, } +#[deriving(PartialEq)] pub enum MoveKind { Declared, // When declared, variables start out "moved". MoveExpr, // Expression or binding that moves a variable @@ -356,7 +357,7 @@ impl MoveData { let path_index = self.move_path(tcx, lp.clone()); match mode { - euv::JustWrite => { + euv::Init | euv::JustWrite => { self.assignee_ids.borrow_mut().insert(assignee_id); } euv::WriteAndRead => { } @@ -537,6 +538,28 @@ impl<'a> FlowedMoveData<'a> { }) } + pub fn kind_of_move_of_path(&self, + id: ast::NodeId, + loan_path: &Rc) + -> Option { + //! Returns the kind of a move of `loan_path` by `id`, if one exists. + + let mut ret = None; + for loan_path_index in self.move_data.path_map.borrow().find(&*loan_path).iter() { + self.dfcx_moves.each_gen_bit_frozen(id, |move_index| { + let move = self.move_data.moves.borrow(); + let move = move.get(move_index); + if move.path == **loan_path_index { + ret = Some(move.kind); + false + } else { + true + } + }); + } + ret + } + pub fn each_move_of(&self, id: ast::NodeId, loan_path: &Rc, diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 467cd726a65..cd71d95bee9 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -80,12 +80,20 @@ pub enum LoanCause { #[deriving(PartialEq,Show)] pub enum ConsumeMode { - Copy, // reference to x where x has a type that copies - Move, // reference to x where x has a type that moves + Copy, // reference to x where x has a type that copies + Move(MoveReason), // reference to x where x has a type that moves +} + +#[deriving(PartialEq,Show)] +pub enum MoveReason { + DirectRefMove, + PatBindingMove, + CaptureMove, } #[deriving(PartialEq,Show)] pub enum MutateMode { + Init, JustWrite, // x = y WriteAndRead, // x += y } @@ -160,7 +168,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { consume_id: ast::NodeId, consume_span: Span, cmt: mc::cmt) { - let mode = copy_or_move(self.tcx(), cmt.ty); + let mode = copy_or_move(self.tcx(), cmt.ty, DirectRefMove); self.delegate.consume(consume_id, consume_span, cmt, mode); } @@ -712,7 +720,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { let def = def_map.borrow().get_copy(&pat.id); match mc.cat_def(pat.id, pat.span, pat_ty, def) { Ok(binding_cmt) => { - delegate.mutate(pat.id, pat.span, binding_cmt, JustWrite); + delegate.mutate(pat.id, pat.span, binding_cmt, Init); } Err(_) => { } } @@ -728,7 +736,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { r, bk, RefBinding); } ast::PatIdent(ast::BindByValue(_), _, _) => { - let mode = copy_or_move(typer.tcx(), cmt_pat.ty); + let mode = copy_or_move(typer.tcx(), cmt_pat.ty, PatBindingMove); delegate.consume_pat(pat, cmt_pat, mode); } _ => { @@ -834,7 +842,8 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { let cmt_var = return_if_err!(self.cat_captured_var(closure_expr.id, closure_expr.span, freevar.def)); - self.delegate_consume(closure_expr.id, freevar.span, cmt_var); + let mode = copy_or_move(self.tcx(), cmt_var.ty, CaptureMove); + self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode); } } @@ -851,7 +860,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> { } } -fn copy_or_move(tcx: &ty::ctxt, ty: ty::t) -> ConsumeMode { - if ty::type_moves_by_default(tcx, ty) { Move } else { Copy } +fn copy_or_move(tcx: &ty::ctxt, ty: ty::t, move_reason: MoveReason) -> ConsumeMode { + if ty::type_moves_by_default(tcx, ty) { Move(move_reason) } else { Copy } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1d2ed72f70e..240212699e4 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -390,12 +390,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { Some(adjustment) => { match *adjustment { ty::AutoObject(..) => { - // Implicity casts a concrete object to trait object - // so just patch up the type + // Implicity cast a concrete object to trait object. + // Result is an rvalue. let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); - let mut expr_cmt = (*if_ok!(self.cat_expr_unadjusted(expr))).clone(); - expr_cmt.ty = expr_ty; - Ok(Rc::new(expr_cmt)) + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } ty::AutoAddEnv(..) => { diff --git a/src/test/compile-fail/borrowck-field-sensitivity.rs b/src/test/compile-fail/borrowck-field-sensitivity.rs new file mode 100644 index 00000000000..4363f85048f --- /dev/null +++ b/src/test/compile-fail/borrowck-field-sensitivity.rs @@ -0,0 +1,124 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct A { a: int, b: Box } + +fn borrow(_: &T) { } + +fn use_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + drop(*x.b); //~ ERROR use of partially moved value: `*x.b` +} + +fn use_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let y = A { a: 3, .. x }; + drop(*x.b); //~ ERROR use of partially moved value: `*x.b` +} + +fn borrow_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + borrow(&x.b); //~ ERROR use of moved value: `x.b` +} + +fn borrow_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let _y = A { a: 3, .. x }; + borrow(&x.b); //~ ERROR use of moved value: `x.b` +} + +fn move_after_borrow() { + let x = A { a: 1, b: box 2 }; + let y = &x.b; + drop(x.b); //~ ERROR cannot move out of `x.b` because it is borrowed + borrow(&*y); +} + +fn fu_move_after_borrow() { + let x = A { a: 1, b: box 2 }; + let y = &x.b; + let _z = A { a: 3, .. x }; //~ ERROR cannot move out of `x.b` because it is borrowed + borrow(&*y); +} + +fn mut_borrow_after_mut_borrow() { + let mut x = A { a: 1, b: box 2 }; + let y = &mut x.a; + let z = &mut x.a; //~ ERROR cannot borrow `x.a` as mutable more than once at a time + drop(*y); + drop(*z); +} + +fn move_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + drop(x.b); //~ ERROR use of moved value: `x.b` +} + +fn move_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let _y = A { a: 3, .. x }; + drop(x.b); //~ ERROR use of moved value: `x.b` +} + +fn fu_move_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + let _z = A { a: 3, .. x }; //~ ERROR use of moved value: `x.b` +} + +fn fu_move_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let _y = A { a: 3, .. x }; + let _z = A { a: 4, .. x }; //~ ERROR use of moved value: `x.b` +} + +// The following functions aren't yet accepted, but they should be. + +fn use_after_field_assign_after_uninit() { + let mut x: A; + x.a = 1; + drop(x.a); //~ ERROR use of possibly uninitialized variable: `x.a` +} + +fn borrow_after_field_assign_after_uninit() { + let mut x: A; + x.a = 1; + borrow(&x.a); //~ ERROR use of possibly uninitialized variable: `x.a` +} + +fn move_after_field_assign_after_uninit() { + let mut x: A; + x.b = box 1; + drop(x.b); //~ ERROR use of possibly uninitialized variable: `x.b` +} + +fn main() { + use_after_move(); + use_after_fu_move(); + + borrow_after_move(); + borrow_after_fu_move(); + move_after_borrow(); + fu_move_after_borrow(); + mut_borrow_after_mut_borrow(); + + move_after_move(); + move_after_fu_move(); + fu_move_after_move(); + fu_move_after_fu_move(); + + use_after_field_assign_after_uninit(); + borrow_after_field_assign_after_uninit(); + move_after_field_assign_after_uninit(); +} + diff --git a/src/test/compile-fail/borrowck-init-in-fru.rs b/src/test/compile-fail/borrowck-init-in-fru.rs index 97fc2b4d44c..6a42989b47b 100644 --- a/src/test/compile-fail/borrowck-init-in-fru.rs +++ b/src/test/compile-fail/borrowck-init-in-fru.rs @@ -16,6 +16,6 @@ struct point { fn main() { let mut origin: point; - origin = point {x: 10,.. origin}; //~ ERROR use of possibly uninitialized variable: `origin` + origin = point {x: 10,.. origin}; //~ ERROR use of possibly uninitialized variable: `origin.y` origin.clone(); } diff --git a/src/test/compile-fail/borrowck-multiple-captures.rs b/src/test/compile-fail/borrowck-multiple-captures.rs new file mode 100644 index 00000000000..e12d2b91479 --- /dev/null +++ b/src/test/compile-fail/borrowck-multiple-captures.rs @@ -0,0 +1,64 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::task; + +fn borrow(_: &T) { } + +fn different_vars_after_borrows() { + let x1 = box 1; + let p1 = &x1; + let x2 = box 2; + let p2 = &x2; + task::spawn(proc() { + drop(x1); //~ ERROR cannot move `x1` into closure because it is borrowed + drop(x2); //~ ERROR cannot move `x2` into closure because it is borrowed + }); + borrow(&*p1); + borrow(&*p2); +} + +fn different_vars_after_moves() { + let x1 = box 1; + drop(x1); + let x2 = box 2; + drop(x2); + task::spawn(proc() { + drop(x1); //~ ERROR capture of moved value: `x1` + drop(x2); //~ ERROR capture of moved value: `x2` + }); +} + +fn same_var_after_borrow() { + let x = box 1; + let p = &x; + task::spawn(proc() { + drop(x); //~ ERROR cannot move `x` into closure because it is borrowed + drop(x); //~ ERROR use of moved value: `x` + }); + borrow(&*p); +} + +fn same_var_after_move() { + let x = box 1; + drop(x); + task::spawn(proc() { + drop(x); //~ ERROR capture of moved value: `x` + drop(x); //~ ERROR use of moved value: `x` + }); +} + +fn main() { + different_vars_after_borrows(); + different_vars_after_moves(); + same_var_after_borrow(); + same_var_after_move(); +} + diff --git a/src/test/compile-fail/liveness-use-after-move.rs b/src/test/compile-fail/liveness-use-after-move.rs index 9927e2ea9da..4b578765f32 100644 --- a/src/test/compile-fail/liveness-use-after-move.rs +++ b/src/test/compile-fail/liveness-use-after-move.rs @@ -13,6 +13,6 @@ extern crate debug; fn main() { let x = box 5; let y = x; - println!("{:?}", *x); //~ ERROR use of moved value: `x` + println!("{:?}", *x); //~ ERROR use of partially moved value: `*x` y.clone(); } diff --git a/src/test/compile-fail/moves-based-on-type-access-to-field.rs b/src/test/compile-fail/moves-based-on-type-access-to-field.rs index 15cf176a0db..f09e80974bd 100644 --- a/src/test/compile-fail/moves-based-on-type-access-to-field.rs +++ b/src/test/compile-fail/moves-based-on-type-access-to-field.rs @@ -17,12 +17,6 @@ struct Foo { f: String, y: int } fn consume(_s: String) {} fn touch(_a: &A) {} -fn f10() { - let x = Foo { f: "hi".to_string(), y: 3 }; - consume(x.f); - touch(&x.y); //~ ERROR use of partially moved value: `x` -} - fn f20() { let x = vec!("hi".to_string()); consume(x.move_iter().next().unwrap()); diff --git a/src/test/compile-fail/moves-sru-moved-field.rs b/src/test/compile-fail/moves-sru-moved-field.rs index 6f8353c6164..8b02740497d 100644 --- a/src/test/compile-fail/moves-sru-moved-field.rs +++ b/src/test/compile-fail/moves-sru-moved-field.rs @@ -26,13 +26,7 @@ fn test0(f: Foo, g: Noncopyable, h: Noncopyable) { fn test1(f: Foo, g: Noncopyable, h: Noncopyable) { // copying move-by-default fields from `f`, so move: let _b = Foo {noncopyable: g, ..f}; - let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of partially moved value: `f` -} - -fn test2(f: Foo, g: Noncopyable) { - // move non-copyable field - let _b = Foo {copied: 22, moved: box 23, ..f}; - let _c = Foo {noncopyable: g, ..f}; //~ ERROR use of partially moved value: `f` + let _c = Foo {noncopyable: h, ..f}; //~ ERROR use of moved value: `f.moved` } fn main() {} diff --git a/src/test/compile-fail/use-after-move-self-based-on-type.rs b/src/test/compile-fail/use-after-move-self-based-on-type.rs index b93fc2680cd..b11650a6a4f 100644 --- a/src/test/compile-fail/use-after-move-self-based-on-type.rs +++ b/src/test/compile-fail/use-after-move-self-based-on-type.rs @@ -19,7 +19,7 @@ impl Drop for S { impl S { pub fn foo(self) -> int { self.bar(); - return self.x; //~ ERROR use of moved value: `self` + return self.x; //~ ERROR use of partially moved value: `self.x` } pub fn bar(self) {} diff --git a/src/test/compile-fail/use-after-move-self.rs b/src/test/compile-fail/use-after-move-self.rs index 8d1ab1bcd94..22c3ec7c341 100644 --- a/src/test/compile-fail/use-after-move-self.rs +++ b/src/test/compile-fail/use-after-move-self.rs @@ -16,7 +16,7 @@ struct S { impl S { pub fn foo(self) -> int { self.bar(); - return *self.x; //~ ERROR use of moved value: `self` + return *self.x; //~ ERROR use of partially moved value: `*self.x` } pub fn bar(self) {} diff --git a/src/test/run-pass/borrowck-field-sensitivity.rs b/src/test/run-pass/borrowck-field-sensitivity.rs new file mode 100644 index 00000000000..3b82f51123f --- /dev/null +++ b/src/test/run-pass/borrowck-field-sensitivity.rs @@ -0,0 +1,223 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct A { a: int, b: Box } +struct B { a: Box, b: Box } + +fn borrow(_: &T) { } + +fn move_after_use() { + let x = A { a: 1, b: box 2 }; + drop(x.a); + drop(x.b); +} + +fn move_after_fu_use() { + let x = A { a: 1, b: box 2 }; + let _y = A { b: box 3, .. x }; + drop(x.b); +} + +fn fu_move_after_use() { + let x = A { a: 1, b: box 2 }; + drop(x.a); + let y = A { a: 3, .. x }; + drop(y.b); +} + +fn fu_move_after_fu_use() { + let x = A { a: 1, b: box 2 }; + let _y = A { b: box 3, .. x }; + let z = A { a: 4, .. x }; + drop(z.b); +} + +fn use_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + drop(x.a); +} + +fn use_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let y = A { a: 3, .. x }; + drop(x.a); + drop(y.b); +} + +fn fu_use_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + let _y = A { b: box 3, .. x }; +} + +fn fu_use_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let y = A { a: 3, .. x }; + let _z = A { b: box 3, .. x }; + drop(y.b); +} + +fn borrow_after_move() { + let x = A { a: 1, b: box 2 }; + drop(x.b); + borrow(&x.a); +} + +fn borrow_after_fu_move() { + let x = A { a: 1, b: box 2 }; + let y = A { a: 3, .. x }; + borrow(&x.a); + drop(y.b); +} + +fn move_after_borrow() { + let x = A { a: 1, b: box 2 }; + borrow(&x.a); + drop(x.b); +} + +fn fu_move_after_borrow() { + let x = A { a: 1, b: box 2 }; + borrow(&x.a); + let y = A { a: 3, .. x }; + drop(y.b); +} + +fn mut_borrow_after_mut_borrow() { + let mut x = A { a: 1, b: box 2 }; + let y = &mut x.a; + let z = &mut x.b; + drop(*y); + drop(**z); +} + +fn move_after_move() { + let x = B { a: box 1, b: box 2 }; + drop(x.a); + drop(x.b); +} + +fn move_after_fu_move() { + let x = B { a: box 1, b: box 2 }; + let y = B { a: box 3, .. x }; + drop(x.a); + drop(y.b); +} + +fn fu_move_after_move() { + let x = B { a: box 1, b: box 2 }; + drop(x.a); + let z = B { a: box 3, .. x }; + drop(z.b); +} + +fn fu_move_after_fu_move() { + let x = B { a: box 1, b: box 2 }; + let y = B { b: box 3, .. x }; + let z = B { a: box 4, .. x }; + drop(y.a); + drop(z.b); +} + +fn use_after_assign_after_move() { + let mut x = A { a: 1, b: box 2 }; + drop(x.b); + x = A { a: 3, b: box 4 }; + drop(*x.b); +} + +fn use_after_field_assign_after_move() { + let mut x = A { a: 1, b: box 2 }; + drop(x.b); + x.b = box 3; + drop(*x.b); +} + +fn borrow_after_assign_after_move() { + let mut x = A { a: 1, b: box 2 }; + drop(x.b); + x = A { a: 3, b: box 4 }; + borrow(&x.b); +} + +fn borrow_after_field_assign_after_move() { + let mut x = A { a: 1, b: box 2 }; + drop(x.b); + x.b = box 3; + borrow(&x.b); +} + +fn move_after_assign_after_move() { + let mut x = A { a: 1, b: box 2 }; + let y = x.b; + x = A { a: 3, b: box 4 }; + drop(x.b); + drop(y); +} + +fn move_after_field_assign_after_move() { + let mut x = A { a: 1, b: box 2 }; + drop(x.b); + x.b = box 3; + drop(x.b); +} + +fn use_after_assign_after_uninit() { + let mut x: A; + x = A { a: 1, b: box 2 }; + drop(x.a); +} + +fn borrow_after_assign_after_uninit() { + let mut x: A; + x = A { a: 1, b: box 2 }; + borrow(&x.a); +} + +fn move_after_assign_after_uninit() { + let mut x: A; + x = A { a: 1, b: box 2 }; + drop(x.b); +} + +fn main() { + move_after_use(); + move_after_fu_use(); + fu_move_after_use(); + fu_move_after_fu_use(); + use_after_move(); + use_after_fu_move(); + fu_use_after_move(); + fu_use_after_fu_move(); + + borrow_after_move(); + borrow_after_fu_move(); + move_after_borrow(); + fu_move_after_borrow(); + mut_borrow_after_mut_borrow(); + + move_after_move(); + move_after_fu_move(); + fu_move_after_move(); + fu_move_after_fu_move(); + + use_after_assign_after_move(); + use_after_field_assign_after_move(); + borrow_after_assign_after_move(); + borrow_after_field_assign_after_move(); + move_after_assign_after_move(); + move_after_field_assign_after_move(); + + use_after_assign_after_uninit(); + borrow_after_assign_after_uninit(); + move_after_assign_after_uninit(); +} +