auto merge of #14318 : zwarich/rust/check-loans-refactor, r=nikomatsakis
I tried to split up the less mechanical changes into separate commits so they are easier to review. One thing I'm not quite sure of is whether `MoveReason` should just be replaced with `move_data::MoveKind`.
This commit is contained in:
commit
61d79175c0
16 changed files with 726 additions and 277 deletions
|
@ -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<LoanPath>) {
|
||||
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<LoanPath>,
|
||||
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<LoanPath>)
|
||||
{
|
||||
/*!
|
||||
* 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<LoanPath>) {
|
||||
/*!
|
||||
* 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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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, ());
|
||||
}
|
||||
|
|
|
@ -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<LoanPath>)
|
||||
-> Option<MoveKind> {
|
||||
//! 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<LoanPath>,
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
@ -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(..) => {
|
||||
|
|
124
src/test/compile-fail/borrowck-field-sensitivity.rs
Normal file
124
src/test/compile-fail/borrowck-field-sensitivity.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
struct A { a: int, b: Box<int> }
|
||||
|
||||
fn borrow<T>(_: &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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
64
src/test/compile-fail/borrowck-multiple-captures.rs
Normal file
64
src/test/compile-fail/borrowck-multiple-captures.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::task;
|
||||
|
||||
fn borrow<T>(_: &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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -17,12 +17,6 @@ struct Foo { f: String, y: int }
|
|||
fn consume(_s: String) {}
|
||||
fn touch<A>(_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());
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
223
src/test/run-pass/borrowck-field-sensitivity.rs
Normal file
223
src/test/run-pass/borrowck-field-sensitivity.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
struct A { a: int, b: Box<int> }
|
||||
struct B { a: Box<int>, b: Box<int> }
|
||||
|
||||
fn borrow<T>(_: &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();
|
||||
}
|
||||
|
Loading…
Reference in a new issue