From d123188b20222c8fc5fbc386bd15100b6f3c80ed Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:32 -0700 Subject: [PATCH 1/9] Clean up check_loans. Refactor a number of functions in check_loans to take node IDs and spans rather than taking expressions directly. Also rename some variables to make them less ambiguous. This is the first step towards using ExprUseVisitor in check_loans, as now some of the interfaces more closely match those used in ExprUseVisitor. --- src/librustc/middle/borrowck/check_loans.rs | 85 ++++++++++++--------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 235775c58f54a..6b8efe55f2430 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -385,24 +385,34 @@ impl<'a> CheckLoanCtxt<'a> { }); } - pub fn check_assignment(&self, expr: &ast::Expr) { + pub fn check_assignment_expr(&self, expr: &ast::Expr) { + let assignment_id = expr.id; + let assignment_span = expr.span; + // 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) { + let assignee_cmt = match self.bccx.tcx.adjustments.borrow().find(&assignment_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())); + self.check_assignment(assignment_id, assignment_span, assignee_cmt); + } + + fn check_assignment(&self, + assignment_id: ast::NodeId, + assignment_span: Span, + assignee_cmt: mc::cmt) { + 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 assignee_cmt.mutbl.is_mutable() { + if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) { if check_for_assignment_to_restricted_or_frozen_location( - self, expr, cmt.clone()) { + 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 +420,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(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 +434,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 +505,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 +516,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 +526,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 +538,7 @@ impl<'a> CheckLoanCtxt<'a> { } Some(cause) => { this.bccx.report_aliasability_violation( - expr.span, + span, MutabilityViolation, cause); return false; @@ -538,13 +548,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 +590,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 +667,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 +682,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( @@ -733,7 +744,7 @@ impl<'a> CheckLoanCtxt<'a> { match freevar_mode { freevars::CaptureByRef => { } freevars::CaptureByValue => { - check_by_move_capture(self, closure_id, freevar, &*var_path); + check_by_move_capture(self, closure_id, freevar.span, &*var_path); } } } @@ -742,14 +753,14 @@ impl<'a> CheckLoanCtxt<'a> { fn check_by_move_capture(this: &CheckLoanCtxt, closure_id: ast::NodeId, - freevar: &freevars::freevar_entry, + freevar_span: Span, 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, + freevar_span, format!("cannot move `{}` into closure \ because it is borrowed", this.bccx.loan_path_to_str( @@ -841,7 +852,7 @@ fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>, } ast::ExprAssign(dest, _) | ast::ExprAssignOp(_, dest, _) => { - this.check_assignment(dest); + this.check_assignment_expr(dest); } ast::ExprCall(f, ref args) => { this.check_call(expr, Some(f), f.span, args.as_slice()); @@ -859,7 +870,7 @@ fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>, } ast::ExprInlineAsm(ref ia) => { for &(_, out) in ia.outputs.iter() { - this.check_assignment(out); + this.check_assignment_expr(out); } } _ => {} From 0339b27e6db07e50bf71edc5eb78133a418a5949 Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:32 -0700 Subject: [PATCH 2/9] Fix mem_categorization to treat an AutoObject adjustment as an rvalue. Currently mem_categorization categorizes an AutoObject adjustment the same as the original expression. This can cause two moves to be generated for the same underlying expression. Currently this isn't a problem in practice, since check_loans doesn't rely on ExprUseVisitor. --- src/librustc/middle/mem_categorization.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1d2ed72f70ea0..240212699e4de 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(..) => { From f63fad5d60f451013a6787fec4db12b77cab018c Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:32 -0700 Subject: [PATCH 3/9] Add an Init mode to MutateMode. This isn't necessary right now, but check_loans needs to be able to distinguish between initialization and writes in the ExprUseVisitor mutate callback. --- src/librustc/middle/borrowck/move_data.rs | 2 +- src/librustc/middle/expr_use_visitor.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index 5a4ec36befcc1..1c72c6d670247 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -356,7 +356,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 => { } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 467cd726a65af..f806fcb1f7ed6 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -86,6 +86,7 @@ pub enum ConsumeMode { #[deriving(PartialEq,Show)] pub enum MutateMode { + Init, JustWrite, // x = y WriteAndRead, // x += y } @@ -712,7 +713,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(_) => { } } From 5ccb7644be0f0ab04a76bf88c93cecb63b1ba20d Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:33 -0700 Subject: [PATCH 4/9] Add a move reason to the Move ConsumeMode. Currently it is not possible to distinguish moves caused by captures in the ExprUseVisitor interface. Since check_Loans needs to make that distinction for generating good diagnostics, this is necessary for check_loans to switch to ExprUseVisitor. --- .../middle/borrowck/gather_loans/mod.rs | 4 ++-- src/librustc/middle/expr_use_visitor.rs | 22 +++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index d34ce7f6a5f24..5192ddc793b6d 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -76,7 +76,7 @@ impl<'a> euv::Delegate for GatherLoanCtxt<'a> { match mode { euv::Copy => { return; } - euv::Move => { } + euv::Move(_) => { } } gather_moves::gather_move_from_expr( @@ -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/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index f806fcb1f7ed6..cd71d95bee948 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -80,8 +80,15 @@ 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)] @@ -161,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); } @@ -729,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); } _ => { @@ -835,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); } } @@ -852,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 } } From 40e3fb4c0b46569b0ab4f5642ed1726915d59e98 Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:33 -0700 Subject: [PATCH 5/9] Use the MoveReason to determine a more precise MoveKind in gather_moves. --- .../middle/borrowck/gather_loans/gather_moves.rs | 9 +++++++-- src/librustc/middle/borrowck/gather_loans/mod.rs | 12 ++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index 16fd03c1aadd4..6df474809779a 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 5192ddc793b6d..003687e7b63f9 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, From 78934b03e39af86e60bdafcfe186b0f88c5b192e Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:33 -0700 Subject: [PATCH 6/9] Add a kind_of_move_of_path method to FlowedMoveData. --- src/librustc/middle/borrowck/move_data.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index 1c72c6d670247..3e38aed20ce89 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -537,6 +537,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, From c53d296e122393830ca239195eb21ec3f49025fe Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:33 -0700 Subject: [PATCH 7/9] Change check_loans to use ExprUseVisitor. --- src/librustc/middle/borrowck/check_loans.rs | 428 +++++++++--------- src/librustc/middle/borrowck/mod.rs | 2 +- src/librustc/middle/borrowck/move_data.rs | 1 + src/test/compile-fail/borrowck-init-in-fru.rs | 2 +- .../compile-fail/liveness-use-after-move.rs | 2 +- .../moves-based-on-type-access-to-field.rs | 6 - .../compile-fail/moves-sru-moved-field.rs | 8 +- .../use-after-move-self-based-on-type.rs | 2 +- src/test/compile-fail/use-after-move-self.rs | 2 +- 9 files changed, 223 insertions(+), 230 deletions(-) diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 6b8efe55f2430..fed1fb9c60992 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); + self.consume_common(consume_id, consume_span, cmt, mode); } - 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 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 visit_pat(&mut self, p: &ast::Pat, _: ()) { - check_loans_in_pat(self, p); + + 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 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; + + 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); } - // FIXME(#10894) should continue recursing - fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {} + 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,32 +564,21 @@ impl<'a> CheckLoanCtxt<'a> { }); } - pub fn check_assignment_expr(&self, expr: &ast::Expr) { - let assignment_id = expr.id; - let assignment_span = expr.span; - - // We don't use cat_expr() here because we don't want to treat - // auto-ref'd parameters in overloaded operators as rvalues. - let assignee_cmt = match self.bccx.tcx.adjustments.borrow().find(&assignment_id) { - None => self.bccx.cat_expr_unadjusted(expr), - Some(adj) => self.bccx.cat_expr_autoderefd(expr, adj) - }; - - self.check_assignment(assignment_id, assignment_span, assignee_cmt); - } - fn check_assignment(&self, assignment_id: ast::NodeId, assignment_span: Span, - assignee_cmt: mc::cmt) { + 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 assignee_cmt.mutbl.is_mutable() { if check_for_aliasable_mutable_writes(self, assignment_span, assignee_cmt.clone()) { - if check_for_assignment_to_restricted_or_frozen_location( - self, assignment_id, 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, assignee_cmt); } @@ -420,7 +588,7 @@ impl<'a> CheckLoanCtxt<'a> { // For immutable local variables, assignments are legal // if they cannot already have been assigned - if self.is_local_variable(assignee_cmt.clone()) { + 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| { @@ -695,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.span, &*var_path); - } - } - } - }); - return; - - fn check_by_move_capture(this: &CheckLoanCtxt, - closure_id: ast::NodeId, - freevar_span: Span, - 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) @@ -805,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_expr(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_expr(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/mod.rs b/src/librustc/middle/borrowck/mod.rs index ce111d3ae2948..0fbcf157dacad 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 3e38aed20ce89..f7c2629233484 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 diff --git a/src/test/compile-fail/borrowck-init-in-fru.rs b/src/test/compile-fail/borrowck-init-in-fru.rs index 97fc2b4d44c37..6a42989b47bba 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/liveness-use-after-move.rs b/src/test/compile-fail/liveness-use-after-move.rs index 9927e2ea9da31..4b578765f32f4 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 15cf176a0dbcb..f09e80974bd42 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 6f8353c61647f..8b02740497df8 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 b93fc2680cd30..b11650a6a4f1f 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 8d1ab1bcd947c..22c3ec7c3417c 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) {} From 74eb4b4b5713be0d80b666f0032d7d7c111db368 Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:33 -0700 Subject: [PATCH 8/9] Add new tests for borrowck field-sensitivity. --- .../borrowck-field-sensitivity.rs | 124 ++++++++++ .../run-pass/borrowck-field-sensitivity.rs | 223 ++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 src/test/compile-fail/borrowck-field-sensitivity.rs create mode 100644 src/test/run-pass/borrowck-field-sensitivity.rs 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 0000000000000..4363f85048f0e --- /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/run-pass/borrowck-field-sensitivity.rs b/src/test/run-pass/borrowck-field-sensitivity.rs new file mode 100644 index 0000000000000..3b82f51123f8d --- /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(); +} + From f1542a6a7da088f99bfe2963d29e6ea4b451b432 Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Fri, 6 Jun 2014 11:59:33 -0700 Subject: [PATCH 9/9] Add a test for borrowck errors with multiple closure captures. When converting check_loans to use ExprUseVisitor I encountered a few issues where the wrong number of errors were being emitted for multiple closure captures, but there is no existing test for this. --- .../borrowck-multiple-captures.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/test/compile-fail/borrowck-multiple-captures.rs 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 0000000000000..e12d2b9147978 --- /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(); +} +