Auto merge of #21971 - pnkfelix:fsk-restrict-fixdsz-array-moves, r=nikomatsakis
Revised version of PR #21930. Restrictions on moves into and out-from fixed-length arrays. (There was only one use of this "feature" in the compiler source.) Note 1: the change to the error message in tests/compile-fail/borrowck-use-in-index-lvalue.rs, where we now report that *w is uninitialized (rather than w), was unintended fallout from the implementation strategy used here. The change appears harmless to me, but I welcome advice on how to bring back the old message, which was slightly cleaner (i.e. less unintelligible) since that the syntactic form *w does not actually appear in the source text. Note 2: the move out-from restriction to only apply to expr[i], and not destructuring bind (e.g. f([a, b, c]: Array) { ... }) since the latter is compatible with nonzeroing drop, AFAICT. [breaking-change]
This commit is contained in:
commit
8661b3dc0f
@ -128,7 +128,7 @@ pub enum PointerKind {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum InteriorKind {
|
||||
InteriorField(FieldName),
|
||||
InteriorElement(ElementKind),
|
||||
InteriorElement(InteriorOffsetKind, ElementKind),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
@ -137,6 +137,12 @@ pub enum FieldName {
|
||||
PositionalField(uint)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum InteriorOffsetKind {
|
||||
Index, // e.g. `array_expr[index_expr]`
|
||||
Pattern, // e.g. `fn foo([_, a, _, _]: [A; 4]) { ... }`
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum ElementKind {
|
||||
VecElement,
|
||||
@ -196,10 +202,12 @@ pub enum deref_kind {
|
||||
deref_interior(InteriorKind),
|
||||
}
|
||||
|
||||
type DerefKindContext = Option<InteriorOffsetKind>;
|
||||
|
||||
// Categorizes a derefable type. Note that we include vectors and strings as
|
||||
// derefable (we model an index as the combination of a deref and then a
|
||||
// pointer adjustment).
|
||||
pub fn deref_kind(t: Ty) -> McResult<deref_kind> {
|
||||
fn deref_kind(t: Ty, context: DerefKindContext) -> McResult<deref_kind> {
|
||||
match t.sty {
|
||||
ty::ty_uniq(_) => {
|
||||
Ok(deref_ptr(Unique))
|
||||
@ -220,7 +228,12 @@ pub fn deref_kind(t: Ty) -> McResult<deref_kind> {
|
||||
}
|
||||
|
||||
ty::ty_vec(_, _) | ty::ty_str => {
|
||||
Ok(deref_interior(InteriorElement(element_kind(t))))
|
||||
// no deref of indexed content without supplying InteriorOffsetKind
|
||||
if let Some(context) = context {
|
||||
Ok(deref_interior(InteriorElement(context, element_kind(t))))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
_ => Err(()),
|
||||
@ -455,7 +468,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
autoderefs,
|
||||
cmt.repr(self.tcx()));
|
||||
for deref in 1..autoderefs + 1 {
|
||||
cmt = try!(self.cat_deref(expr, cmt, deref));
|
||||
cmt = try!(self.cat_deref(expr, cmt, deref, None));
|
||||
}
|
||||
return Ok(cmt);
|
||||
}
|
||||
@ -467,7 +480,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
match expr.node {
|
||||
ast::ExprUnary(ast::UnDeref, ref e_base) => {
|
||||
let base_cmt = try!(self.cat_expr(&**e_base));
|
||||
self.cat_deref(expr, base_cmt, 0)
|
||||
self.cat_deref(expr, base_cmt, 0, None)
|
||||
}
|
||||
|
||||
ast::ExprField(ref base, f_name) => {
|
||||
@ -486,6 +499,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
|
||||
ast::ExprIndex(ref base, _) => {
|
||||
let method_call = ty::MethodCall::expr(expr.id());
|
||||
let context = InteriorOffsetKind::Index;
|
||||
match self.typer.node_method_ty(method_call) {
|
||||
Some(method_ty) => {
|
||||
// If this is an index implemented by a method call, then it
|
||||
@ -507,10 +521,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
// is an rvalue. That is what we will be
|
||||
// dereferencing.
|
||||
let base_cmt = self.cat_rvalue_node(expr.id(), expr.span(), ret_ty);
|
||||
self.cat_deref_common(expr, base_cmt, 1, elem_ty, true)
|
||||
self.cat_deref_common(expr, base_cmt, 1, elem_ty, Some(context), true)
|
||||
}
|
||||
None => {
|
||||
self.cat_index(expr, try!(self.cat_expr(&**base)))
|
||||
self.cat_index(expr, try!(self.cat_expr(&**base)), context)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -854,7 +868,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
fn cat_deref<N:ast_node>(&self,
|
||||
node: &N,
|
||||
base_cmt: cmt<'tcx>,
|
||||
deref_cnt: uint)
|
||||
deref_cnt: uint,
|
||||
deref_context: DerefKindContext)
|
||||
-> McResult<cmt<'tcx>> {
|
||||
let adjustment = match self.typer.adjustments().borrow().get(&node.id()) {
|
||||
Some(adj) if ty::adjust_is_object(adj) => ty::AutoObject,
|
||||
@ -882,7 +897,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
};
|
||||
let base_cmt_ty = base_cmt.ty;
|
||||
match ty::deref(base_cmt_ty, true) {
|
||||
Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty,
|
||||
Some(mt) => self.cat_deref_common(node, base_cmt, deref_cnt,
|
||||
mt.ty,
|
||||
deref_context,
|
||||
/* implicit: */ false),
|
||||
None => {
|
||||
debug!("Explicit deref of non-derefable type: {}",
|
||||
@ -897,10 +914,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
base_cmt: cmt<'tcx>,
|
||||
deref_cnt: uint,
|
||||
deref_ty: Ty<'tcx>,
|
||||
deref_context: DerefKindContext,
|
||||
implicit: bool)
|
||||
-> McResult<cmt<'tcx>>
|
||||
{
|
||||
let (m, cat) = match try!(deref_kind(base_cmt.ty)) {
|
||||
let (m, cat) = match try!(deref_kind(base_cmt.ty, deref_context)) {
|
||||
deref_ptr(ptr) => {
|
||||
let ptr = if implicit {
|
||||
match ptr {
|
||||
@ -932,7 +950,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
|
||||
pub fn cat_index<N:ast_node>(&self,
|
||||
elt: &N,
|
||||
mut base_cmt: cmt<'tcx>)
|
||||
mut base_cmt: cmt<'tcx>,
|
||||
context: InteriorOffsetKind)
|
||||
-> McResult<cmt<'tcx>> {
|
||||
//! Creates a cmt for an indexing operation (`[]`).
|
||||
//!
|
||||
@ -974,18 +993,21 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
};
|
||||
|
||||
let m = base_cmt.mutbl.inherit();
|
||||
return Ok(interior(elt, base_cmt.clone(), base_cmt.ty, m, element_ty));
|
||||
return Ok(interior(elt, base_cmt.clone(), base_cmt.ty,
|
||||
m, context, element_ty));
|
||||
|
||||
fn interior<'tcx, N: ast_node>(elt: &N,
|
||||
of_cmt: cmt<'tcx>,
|
||||
vec_ty: Ty<'tcx>,
|
||||
mutbl: MutabilityCategory,
|
||||
context: InteriorOffsetKind,
|
||||
element_ty: Ty<'tcx>) -> cmt<'tcx>
|
||||
{
|
||||
let interior_elem = InteriorElement(context, element_kind(vec_ty));
|
||||
Rc::new(cmt_ {
|
||||
id:elt.id(),
|
||||
span:elt.span(),
|
||||
cat:cat_interior(of_cmt, InteriorElement(element_kind(vec_ty))),
|
||||
cat:cat_interior(of_cmt, interior_elem),
|
||||
mutbl:mutbl,
|
||||
ty:element_ty,
|
||||
note: NoteNone
|
||||
@ -997,10 +1019,11 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
// underlying vec.
|
||||
fn deref_vec<N:ast_node>(&self,
|
||||
elt: &N,
|
||||
base_cmt: cmt<'tcx>)
|
||||
base_cmt: cmt<'tcx>,
|
||||
context: InteriorOffsetKind)
|
||||
-> McResult<cmt<'tcx>>
|
||||
{
|
||||
match try!(deref_kind(base_cmt.ty)) {
|
||||
match try!(deref_kind(base_cmt.ty, Some(context))) {
|
||||
deref_ptr(ptr) => {
|
||||
// for unique ptrs, we inherit mutability from the
|
||||
// owning reference.
|
||||
@ -1041,7 +1064,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(),
|
||||
slice_pat,
|
||||
slice_ty);
|
||||
let cmt_slice = try!(self.cat_index(slice_pat, try!(self.deref_vec(slice_pat, vec_cmt))));
|
||||
let context = InteriorOffsetKind::Pattern;
|
||||
let cmt_vec = try!(self.deref_vec(slice_pat, vec_cmt, context));
|
||||
let cmt_slice = try!(self.cat_index(slice_pat, cmt_vec, context));
|
||||
return Ok((cmt_slice, slice_mutbl, slice_r));
|
||||
|
||||
/// In a pattern like [a, b, ..c], normally `c` has slice type, but if you have [a, b,
|
||||
@ -1253,12 +1278,14 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
||||
// box p1, &p1, &mut p1. we can ignore the mutability of
|
||||
// PatRegion since that information is already contained
|
||||
// in the type.
|
||||
let subcmt = try!(self.cat_deref(pat, cmt, 0));
|
||||
let subcmt = try!(self.cat_deref(pat, cmt, 0, None));
|
||||
try!(self.cat_pattern_(subcmt, &**subpat, op));
|
||||
}
|
||||
|
||||
ast::PatVec(ref before, ref slice, ref after) => {
|
||||
let elt_cmt = try!(self.cat_index(pat, try!(self.deref_vec(pat, cmt))));
|
||||
let context = InteriorOffsetKind::Pattern;
|
||||
let vec_cmt = try!(self.deref_vec(pat, cmt, context));
|
||||
let elt_cmt = try!(self.cat_index(pat, vec_cmt, context));
|
||||
for before_pat in before {
|
||||
try!(self.cat_pattern_(elt_cmt.clone(), &**before_pat, op));
|
||||
}
|
||||
@ -1455,10 +1482,18 @@ impl<'tcx> cmt_<'tcx> {
|
||||
cat_interior(_, InteriorField(PositionalField(_))) => {
|
||||
"anonymous field".to_string()
|
||||
}
|
||||
cat_interior(_, InteriorElement(VecElement)) |
|
||||
cat_interior(_, InteriorElement(OtherElement)) => {
|
||||
cat_interior(_, InteriorElement(InteriorOffsetKind::Index,
|
||||
VecElement)) |
|
||||
cat_interior(_, InteriorElement(InteriorOffsetKind::Index,
|
||||
OtherElement)) => {
|
||||
"indexed content".to_string()
|
||||
}
|
||||
cat_interior(_, InteriorElement(InteriorOffsetKind::Pattern,
|
||||
VecElement)) |
|
||||
cat_interior(_, InteriorElement(InteriorOffsetKind::Pattern,
|
||||
OtherElement)) => {
|
||||
"pattern-bound indexed content".to_string()
|
||||
}
|
||||
cat_upvar(ref var) => {
|
||||
var.user_string(tcx)
|
||||
}
|
||||
@ -1546,7 +1581,7 @@ impl<'tcx> Repr<'tcx> for InteriorKind {
|
||||
token::get_name(fld).to_string()
|
||||
}
|
||||
InteriorField(PositionalField(i)) => format!("#{}", i),
|
||||
InteriorElement(_) => "[]".to_string(),
|
||||
InteriorElement(..) => "[]".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
use self::UseError::*;
|
||||
|
||||
use borrowck::*;
|
||||
use borrowck::InteriorKind::{InteriorElement, InteriorField};
|
||||
use rustc::middle::expr_use_visitor as euv;
|
||||
use rustc::middle::mem_categorization as mc;
|
||||
use rustc::middle::region;
|
||||
@ -743,15 +744,16 @@ 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(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(InteriorElement(..))) |
|
||||
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);
|
||||
// assigning to `P[i]` requires `P` is initialized
|
||||
// assigning to `(*P)` requires `P` is initialized
|
||||
self.check_if_path_is_moved(id, span, use_kind, lp_base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
use self::Fragment::*;
|
||||
|
||||
use borrowck::InteriorKind::{InteriorField, InteriorElement};
|
||||
use borrowck::{LoanPath};
|
||||
use borrowck::LoanPathKind::{LpVar, LpUpvar, LpDowncast, LpExtend};
|
||||
use borrowck::LoanPathElem::{LpDeref, LpInterior};
|
||||
@ -300,12 +301,16 @@ fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
|
||||
LpExtend(_, _, LpDeref(mc::Implicit(..))) |
|
||||
LpExtend(_, _, LpDeref(mc::BorrowedPtr(..))) => {}
|
||||
|
||||
// FIXME(pnkfelix): LV[j] should be tracked, at least in the
|
||||
// FIXME (pnkfelix): LV[j] should be tracked, at least in the
|
||||
// sense of we will track the remaining drop obligation of the
|
||||
// rest of the array.
|
||||
//
|
||||
// LV[j] is not tracked precisely
|
||||
LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
|
||||
// Well, either that or LV[j] should be made illegal.
|
||||
// But even then, we will need to deal with destructuring
|
||||
// bind.
|
||||
//
|
||||
// Anyway, for now: LV[j] is not tracked precisely
|
||||
LpExtend(_, _, LpInterior(InteriorElement(..))) => {
|
||||
let mp = this.move_path(tcx, lp.clone());
|
||||
gathered_fragments.push(AllButOneFrom(mp));
|
||||
}
|
||||
@ -313,7 +318,7 @@ fn add_fragment_siblings<'tcx>(this: &MoveData<'tcx>,
|
||||
// field access LV.x and tuple access LV#k are the cases
|
||||
// we are interested in
|
||||
LpExtend(ref loan_parent, mc,
|
||||
LpInterior(mc::InteriorField(ref field_name))) => {
|
||||
LpInterior(InteriorField(ref field_name))) => {
|
||||
let enum_variant_info = match loan_parent.kind {
|
||||
LpDowncast(ref loan_parent_2, variant_def_id) =>
|
||||
Some((variant_def_id, loan_parent_2.clone())),
|
||||
@ -452,7 +457,7 @@ fn add_fragment_sibling_core<'tcx>(this: &MoveData<'tcx>,
|
||||
LpVar(..) | LpUpvar(..) | LpExtend(..) => enum_variant_did,
|
||||
};
|
||||
|
||||
let loan_path_elem = LpInterior(mc::InteriorField(new_field_name));
|
||||
let loan_path_elem = LpInterior(InteriorField(new_field_name));
|
||||
let new_lp_type = match new_field_name {
|
||||
mc::NamedField(ast_name) =>
|
||||
ty::named_element_ty(tcx, parent.to_type(), ast_name, opt_variant_did),
|
||||
|
@ -16,6 +16,8 @@ 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::mem_categorization::InteriorOffsetKind as Kind;
|
||||
use rustc::middle::ty;
|
||||
use rustc::util::ppaux::Repr;
|
||||
use std::rc::Rc;
|
||||
@ -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>> {
|
||||
@ -174,7 +177,8 @@ 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(_)) |
|
||||
mc::cat_interior(ref b, mc::InteriorElement(Kind::Pattern, _)) => {
|
||||
match b.ty.sty {
|
||||
ty::ty_struct(did, _) | ty::ty_enum(did, _) => {
|
||||
if ty::has_dtor(bccx.tcx, did) {
|
||||
@ -189,6 +193,11 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
|
||||
}
|
||||
}
|
||||
|
||||
mc::cat_interior(_, mc::InteriorElement(Kind::Index, _)) => {
|
||||
// Forbid move of arr[i] for arr: [T; 3]; see RFC 533.
|
||||
Some(cmt.clone())
|
||||
}
|
||||
|
||||
mc::cat_deref(ref b, _, mc::Unique) => {
|
||||
check_and_get_illegal_move_origin(bccx, b)
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
use borrowck::BorrowckCtxt;
|
||||
use rustc::middle::mem_categorization as mc;
|
||||
use rustc::middle::mem_categorization::Typer;
|
||||
use rustc::middle::mem_categorization::InteriorOffsetKind as Kind;
|
||||
use rustc::middle::ty;
|
||||
use rustc::util::ppaux::UserString;
|
||||
use std::cell::RefCell;
|
||||
@ -110,6 +112,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 {
|
||||
@ -121,8 +124,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(Kind::Index, _)) => {
|
||||
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) => {
|
||||
|
@ -19,6 +19,8 @@ use rustc::middle::ty;
|
||||
use rustc::util::ppaux::Repr;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use borrowck::ToInteriorKind;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -96,7 +98,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
|
||||
// the memory, so no additional restrictions are
|
||||
// needed.
|
||||
let result = self.restrict(cmt_base);
|
||||
self.extend(result, &cmt, LpInterior(i))
|
||||
self.extend(result, &cmt, LpInterior(i.cleaned()))
|
||||
}
|
||||
|
||||
mc::cat_static_item(..) => {
|
||||
|
@ -18,6 +18,8 @@ pub use self::bckerr_code::*;
|
||||
pub use self::AliasableViolationKind::*;
|
||||
pub use self::MovedValueUseKind::*;
|
||||
|
||||
use self::InteriorKind::*;
|
||||
|
||||
use rustc::middle::cfg;
|
||||
use rustc::middle::dataflow::DataFlowContext;
|
||||
use rustc::middle::dataflow::BitwiseOperator;
|
||||
@ -314,10 +316,30 @@ impl<'tcx> LoanPath<'tcx> {
|
||||
// b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
|
||||
static DOWNCAST_PRINTED_OPERATOR : &'static str = " as ";
|
||||
|
||||
// A local, "cleaned" version of `mc::InteriorKind` that drops
|
||||
// information that is not relevant to loan-path analysis. (In
|
||||
// particular, the distinction between how precisely a array-element
|
||||
// is tracked is irrelevant here.)
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum InteriorKind {
|
||||
InteriorField(mc::FieldName),
|
||||
InteriorElement(mc::ElementKind),
|
||||
}
|
||||
|
||||
trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
|
||||
impl ToInteriorKind for mc::InteriorKind {
|
||||
fn cleaned(self) -> InteriorKind {
|
||||
match self {
|
||||
mc::InteriorField(name) => InteriorField(name),
|
||||
mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum LoanPathElem {
|
||||
LpDeref(mc::PointerKind), // `*LV` in doc.rs
|
||||
LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
|
||||
LpInterior(InteriorKind), // `LV.f` in doc.rs
|
||||
}
|
||||
|
||||
pub fn closure_to_block(closure_id: ast::NodeId,
|
||||
@ -446,7 +468,7 @@ pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
|
||||
|
||||
mc::cat_interior(ref cmt_base, ik) => {
|
||||
opt_loan_path(cmt_base).map(|lp| {
|
||||
new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
|
||||
new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik.cleaned())))
|
||||
})
|
||||
}
|
||||
|
||||
@ -918,7 +940,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
|
||||
LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
|
||||
LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => {
|
||||
self.append_autoderefd_loan_path_to_string(&**lp_base, out);
|
||||
match fname {
|
||||
mc::NamedField(fname) => {
|
||||
@ -932,7 +954,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
|
||||
LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => {
|
||||
self.append_autoderefd_loan_path_to_string(&**lp_base, out);
|
||||
out.push_str("[..]");
|
||||
}
|
||||
@ -1006,6 +1028,17 @@ impl DataFlowOperator for LoanDataFlowOperator {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Repr<'tcx> for InteriorKind {
|
||||
fn repr(&self, _tcx: &ty::ctxt<'tcx>) -> String {
|
||||
match *self {
|
||||
InteriorField(mc::NamedField(fld)) =>
|
||||
format!("{}", token::get_name(fld)),
|
||||
InteriorField(mc::PositionalField(i)) => format!("#{}", i),
|
||||
InteriorElement(..) => "[]".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Repr<'tcx> for Loan<'tcx> {
|
||||
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
|
||||
format!("Loan_{}({}, {:?}, {:?}-{:?}, {})",
|
||||
|
@ -19,7 +19,6 @@ use rustc::middle::dataflow::DataFlowContext;
|
||||
use rustc::middle::dataflow::BitwiseOperator;
|
||||
use rustc::middle::dataflow::DataFlowOperator;
|
||||
use rustc::middle::expr_use_visitor as euv;
|
||||
use rustc::middle::mem_categorization as mc;
|
||||
use rustc::middle::ty;
|
||||
use rustc::util::nodemap::{FnvHashMap, NodeSet};
|
||||
use rustc::util::ppaux::Repr;
|
||||
@ -193,9 +192,13 @@ fn loan_path_is_precise(loan_path: &LoanPath) -> bool {
|
||||
LpVar(_) | LpUpvar(_) => {
|
||||
true
|
||||
}
|
||||
LpExtend(_, _, LpInterior(mc::InteriorElement(_))) => {
|
||||
// Paths involving element accesses do not refer to a unique
|
||||
LpExtend(_, _, LpInterior(InteriorKind::InteriorElement(..))) => {
|
||||
// Paths involving element accesses a[i] do not refer to a unique
|
||||
// location, as there is no accurate tracking of the indices.
|
||||
//
|
||||
// (Paths involving element accesses via slice pattern bindings
|
||||
// can in principle be tracked precisely, but that is future
|
||||
// work. For now, continue claiming that they are imprecise.)
|
||||
false
|
||||
}
|
||||
LpDowncast(ref lp_base, _) |
|
||||
|
@ -924,13 +924,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()
|
||||
|
@ -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(); }
|
||||
|
@ -14,6 +14,8 @@ fn main() {
|
||||
[1, 2, tail..] => tail,
|
||||
_ => unreachable!()
|
||||
};
|
||||
a[0] = 0; //~ ERROR cannot assign to `a[..]` because it is borrowed
|
||||
println!("t[0]: {}", t[0]);
|
||||
a[2] = 0; //~ ERROR cannot assign to `a[..]` because it is borrowed
|
||||
println!("t[0]: {}", t[0]);
|
||||
t[0];
|
||||
}
|
||||
|
@ -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() { }
|
||||
|
@ -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`
|
||||
}
|
25
src/test/compile-fail/move-into-dead-array-2.rs
Normal file
25
src/test/compile-fail/move-into-dead-array-2.rs
Normal file
@ -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
src/test/compile-fail/move-out-of-array-1.rs
Normal file
28
src/test/compile-fail/move-out-of-array-1.rs
Normal file
@ -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
src/test/run-pass/copy-out-of-array-1.rs
Normal file
28
src/test/run-pass/copy-out-of-array-1.rs
Normal file
@ -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
src/test/run-pass/destructure-array-1.rs
Normal file
35
src/test/run-pass/destructure-array-1.rs
Normal file
@ -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"),
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user