Skip to content

Restrictions on moves into and out-from fixed-length arrays. #21930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/librustc_borrowck/borrowck/check_loans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
23 changes: 22 additions & 1 deletion src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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<mc::cmt<'tcx>> {
Expand All @@ -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) {
Expand All @@ -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)
}
Expand Down
14 changes: 13 additions & 1 deletion src/librustc_borrowck/borrowck/gather_loans/move_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -110,6 +111,7 @@ fn group_errors_with_same_origin<'tcx>(errors: &Vec<MoveError<'tcx>>)
}
}

// (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 {
Expand All @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions src/test/compile-fail/borrowck-use-in-index-lvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(); }
53 changes: 0 additions & 53 deletions src/test/compile-fail/move-fragments-9.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)`
Expand All @@ -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() { }
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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`
}
25 changes: 25 additions & 0 deletions src/test/compile-fail/move-into-dead-array-2.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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`
}
28 changes: 28 additions & 0 deletions src/test/compile-fail/move-out-of-array-1.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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]`
}
28 changes: 28 additions & 0 deletions src/test/run-pass/copy-out-of-array-1.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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]
}
35 changes: 35 additions & 0 deletions src/test/run-pass/destructure-array-1.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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"),
}
}