diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index b66419420e9a1..52e3d93402d94 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -743,13 +743,14 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { self.check_if_assigned_path_is_moved(id, span, use_kind, lp_base); } - LpExtend(ref lp_base, _, LpInterior(_)) => { + LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(_))) => { // 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, _, LpInterior(mc::InteriorElement(_))) | LpExtend(ref lp_base, _, LpDeref(_)) => { - // assigning to `(*P)` requires that `P` be initialized + // assigning to `(*P)` or `P[i]` requires `P` is initialized self.check_if_path_is_moved(id, span, use_kind, lp_base); } diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs index ca1fba53de42a..7864a66fce57e 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs @@ -16,10 +16,12 @@ use borrowck::gather_loans::move_error::{MoveError, MoveErrorCollector}; use borrowck::move_data::*; use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization as mc; +use rustc::middle::mem_categorization::Typer; use rustc::middle::ty; use rustc::util::ppaux::Repr; use std::rc::Rc; use syntax::ast; +use syntax::ast_map; use syntax::codemap::Span; struct GatherMoveInfo<'tcx> { @@ -156,6 +158,7 @@ pub fn gather_assignment<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, mode); } +// (keep in sync with move_error::report_cannot_move_out_of ) fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, cmt: &mc::cmt<'tcx>) -> Option> { @@ -174,7 +177,7 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, } mc::cat_downcast(ref b, _) | - mc::cat_interior(ref b, _) => { + mc::cat_interior(ref b, mc::InteriorField(_)) => { match b.ty.sty { ty::ty_struct(did, _) | ty::ty_enum(did, _) => { if ty::has_dtor(bccx.tcx, did) { @@ -189,6 +192,24 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, } } + mc::cat_interior(ref b, mc::InteriorElement(_)) => { + // We allow `fn f([a, b, c]: [T; 3]) { ... }`, but + // forbid `fn f(arr: [T; 3]) { ... arr[i] ... }`. + // + // Both use the same kind of cmt, so lookup its cmt.id + // in the ast map. + let node = bccx.tcx.map.find(cmt.id); + if let Some(ast_map::NodeExpr(expr)) = node { + if let ast::ExprIndex(..) = expr.node { + Some(cmt.clone()) + } else { + check_and_get_illegal_move_origin(bccx, b) + } + } else { + check_and_get_illegal_move_origin(bccx, b) + } + } + mc::cat_deref(ref b, _, mc::Unique) => { check_and_get_illegal_move_origin(bccx, b) } diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index 28d02161eebd7..39f37fcb80649 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -10,6 +10,7 @@ use borrowck::BorrowckCtxt; use rustc::middle::mem_categorization as mc; +use rustc::middle::mem_categorization::Typer; use rustc::middle::ty; use rustc::util::ppaux::UserString; use std::cell::RefCell; @@ -110,6 +111,7 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec>) } } +// (keep in sync with gather_moves::check_and_get_illegal_move_origin ) fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, move_from: mc::cmt<'tcx>) { match move_from.cat { @@ -121,8 +123,18 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, move_from.descriptive_string(bccx.tcx))[]); } + mc::cat_interior(ref b, mc::InteriorElement(_)) => { + let expr = bccx.tcx.map.expect_expr(move_from.id); + if let ast::ExprIndex(..) = expr.node { + bccx.span_err(move_from.span, + &format!("cannot move out of type `{}`, \ + a non-copy fixed-size array", + b.ty.user_string(bccx.tcx))[]); + } + } + mc::cat_downcast(ref b, _) | - mc::cat_interior(ref b, _) => { + mc::cat_interior(ref b, mc::InteriorField(_)) => { match b.ty.sty { ty::ty_struct(did, _) | ty::ty_enum(did, _) if ty::has_dtor(bccx.tcx, did) => { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d7283db25a5f2..667d3d5f65381 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -925,13 +925,13 @@ impl TokenTree { let v = [TtToken(sp, token::Dollar), TtToken(sp, token::Ident(token::str_to_ident(var.as_str()), token::Plain))]; - v[index] + v[index].clone() } (&TtToken(sp, token::MatchNt(name, kind, name_st, kind_st)), _) => { let v = [TtToken(sp, token::SubstNt(name, name_st)), TtToken(sp, token::Colon), TtToken(sp, token::Ident(kind, kind_st))]; - v[index] + v[index].clone() } (&TtSequence(_, ref seq), _) => { seq.tts[index].clone() diff --git a/src/test/compile-fail/borrowck-use-in-index-lvalue.rs b/src/test/compile-fail/borrowck-use-in-index-lvalue.rs index 94c1d3a6a4505..7291bcd2ce126 100644 --- a/src/test/compile-fail/borrowck-use-in-index-lvalue.rs +++ b/src/test/compile-fail/borrowck-use-in-index-lvalue.rs @@ -10,10 +10,10 @@ fn test() { let w: &mut [isize]; - w[5] = 0; //~ ERROR use of possibly uninitialized variable: `w` + w[5] = 0; //~ ERROR use of possibly uninitialized variable: `*w` let mut w: &mut [isize]; - w[5] = 0; //~ ERROR use of possibly uninitialized variable: `w` + w[5] = 0; //~ ERROR use of possibly uninitialized variable: `*w` } fn main() { test(); } diff --git a/src/test/compile-fail/move-fragments-9.rs b/src/test/compile-fail/move-fragments-9.rs index d0eeebd02f809..426d5fa29a020 100644 --- a/src/test/compile-fail/move-fragments-9.rs +++ b/src/test/compile-fail/move-fragments-9.rs @@ -33,15 +33,6 @@ pub fn test_move_array_into_recv(a: [D; 3], recv: &mut [D; 3]) { *recv = a; } -#[rustc_move_fragments] -pub fn test_extract_array_elem(a: [D; 3], i: usize) -> D { - //~^ ERROR parent_of_fragments: `$(local a)` - //~| ERROR assigned_leaf_path: `$(local i)` - //~| ERROR moved_leaf_path: `$(local a).[]` - //~| ERROR unmoved_fragment: `$(allbutone $(local a).[])` - a[i] -} - #[rustc_move_fragments] pub fn test_overwrite_array_elem(mut a: [D; 3], i: usize, d: D) { //~^ ERROR parent_of_fragments: `$(local mut a)` @@ -53,48 +44,4 @@ pub fn test_overwrite_array_elem(mut a: [D; 3], i: usize, d: D) { a[i] = d; } -// FIXME (pnkfelix): Both test_move_array_then_overwrite_elem1 and -// test_move_array_then_overwrite_elem2 illustrate a behavior that -// we need to make illegal if we want to get rid of drop-flags. -// See RFC PR 320 for more discussion. - -#[rustc_move_fragments] -pub fn test_move_array_then_overwrite_elem1(mut a: [D; 3], i: usize, recv: &mut [D; 3], d: D) { - //~^ ERROR parent_of_fragments: `$(local mut a)` - //~| ERROR parent_of_fragments: `$(local recv)` - //~| ERROR assigned_leaf_path: `$(local recv).*` - //~| ERROR assigned_leaf_path: `$(local i)` - //~| ERROR assigned_leaf_path: `$(local d)` - //~| ERROR moved_leaf_path: `$(local d)` - //~| ERROR assigned_leaf_path: `$(local mut a).[]` - //~| ERROR unmoved_fragment: `$(allbutone $(local mut a).[])` - - // This test covers the case where the array contents have been all moved away, but - // we still need to deal with new initializing writes into the array. - *recv = a; - a[i] = d; -} - -#[rustc_move_fragments] -pub fn test_move_array_then_overwrite_elem2(mut a: [D; 3], i: usize, j: usize, - recv: &mut [D; 3], d1: D, d2: D) { - //~^^ ERROR parent_of_fragments: `$(local mut a)` - //~| ERROR parent_of_fragments: `$(local recv)` - //~| ERROR assigned_leaf_path: `$(local recv).*` - //~| ERROR assigned_leaf_path: `$(local i)` - //~| ERROR assigned_leaf_path: `$(local j)` - //~| ERROR assigned_leaf_path: `$(local d1)` - //~| ERROR assigned_leaf_path: `$(local d2)` - //~| ERROR moved_leaf_path: `$(local d1)` - //~| ERROR moved_leaf_path: `$(local d2)` - //~| ERROR assigned_leaf_path: `$(local mut a).[]` - //~| ERROR unmoved_fragment: `$(allbutone $(local mut a).[])` - - // This test covers the case where the array contents have been all moved away, but - // we still need to deal with new initializing writes into the array. - *recv = a; - a[i] = d1; - a[j] = d2; -} - pub fn main() { } diff --git a/src/test/compile-fail/borrowck-array-double-move.rs b/src/test/compile-fail/move-into-dead-array-1.rs similarity index 56% rename from src/test/compile-fail/borrowck-array-double-move.rs rename to src/test/compile-fail/move-into-dead-array-1.rs index 3fb42b38e842c..61ccb694fd212 100644 --- a/src/test/compile-fail/borrowck-array-double-move.rs +++ b/src/test/compile-fail/move-into-dead-array-1.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,17 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(unknown_features)] -#![feature(box_syntax)] +// Ensure that we cannot move into an uninitialized fixed-size array. -fn f() { - let mut a = [box 0, box 1]; - drop(a[0]); - a[1] = box 2; - drop(a[0]); //~ ERROR use of moved value: `a[..]` -} +struct D { _x: u8 } + +fn d() -> D { D { _x: 0 } } fn main() { - f(); + foo(1); + foo(3); } +fn foo(i: usize) { + let mut a: [D; 4]; + a[i] = d(); //~ ERROR use of possibly uninitialized variable: `a` +} diff --git a/src/test/compile-fail/move-into-dead-array-2.rs b/src/test/compile-fail/move-into-dead-array-2.rs new file mode 100644 index 0000000000000..d484837c00136 --- /dev/null +++ b/src/test/compile-fail/move-into-dead-array-2.rs @@ -0,0 +1,25 @@ +// Copyright 2015 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. + +// Ensure that we cannot move into an uninitialized fixed-size array. + +struct D { _x: u8 } + +fn d() -> D { D { _x: 0 } } + +fn main() { + foo([d(), d(), d(), d()], 1); + foo([d(), d(), d(), d()], 3); +} + +fn foo(mut a: [D; 4], i: usize) { + drop(a); + a[i] = d(); //~ ERROR use of moved value: `a` +} diff --git a/src/test/compile-fail/move-out-of-array-1.rs b/src/test/compile-fail/move-out-of-array-1.rs new file mode 100644 index 0000000000000..148dec0282331 --- /dev/null +++ b/src/test/compile-fail/move-out-of-array-1.rs @@ -0,0 +1,28 @@ +// Copyright 2015 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. + +// Ensure that we cannot move out of a fixed-size array (especially +// when the element type has a destructor). + + +struct D { _x: u8 } + +impl Drop for D { fn drop(&mut self) { } } + +fn main() { + fn d() -> D { D { _x: 0 } } + + let _d1 = foo([d(), d(), d(), d()], 1); + let _d3 = foo([d(), d(), d(), d()], 3); +} + +fn foo(a: [D; 4], i: usize) -> D { + a[i] //~ ERROR cannot move out of type `[D; 4]` +} diff --git a/src/test/run-pass/copy-out-of-array-1.rs b/src/test/run-pass/copy-out-of-array-1.rs new file mode 100644 index 0000000000000..2b57c1ea0da69 --- /dev/null +++ b/src/test/run-pass/copy-out-of-array-1.rs @@ -0,0 +1,28 @@ +// Copyright 2015 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. + +// Ensure that we can copy out of a fixed-size array. +// +// (Compare with compile-fail/move-out-of-array-1.rs) + +struct C { _x: u8 } + +impl Copy for C { } + +fn main() { + fn d() -> C { C { _x: 0 } } + + let _d1 = foo([d(), d(), d(), d()], 1); + let _d3 = foo([d(), d(), d(), d()], 3); +} + +fn foo(a: [C; 4], i: usize) -> C { + a[i] +} diff --git a/src/test/run-pass/destructure-array-1.rs b/src/test/run-pass/destructure-array-1.rs new file mode 100644 index 0000000000000..43271162c1812 --- /dev/null +++ b/src/test/run-pass/destructure-array-1.rs @@ -0,0 +1,35 @@ +// Copyright 2015 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. + +// Ensure that we can do a destructuring bind of a fixed-size array, +// even when the element type has a destructor. + +struct D { x: u8 } + +impl Drop for D { fn drop(&mut self) { } } + +fn main() { + fn d(x: u8) -> D { D { x: x } } + + let d1 = foo([d(1), d(2), d(3), d(4)], 1); + let d3 = foo([d(5), d(6), d(7), d(8)], 3); + assert_eq!(d1.x, 2); + assert_eq!(d3.x, 8); +} + +fn foo([a, b, c, d]: [D; 4], i: usize) -> D { + match i { + 0 => a, + 1 => b, + 2 => c, + 3 => d, + _ => panic!("unmatched"), + } +}