From c0a874472a863058ac39654626bf62c2d953b27b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Feb 2015 02:34:39 +0100 Subject: [PATCH 1/5] Fix libsyntax so we can bootstrap even with move out of array restriction. --- src/libsyntax/ast.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 7e1bf7a2230b8..71259ff5d9ade 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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() From cc8f35f874c81be1a5950d2527902b7dd5a94513 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Feb 2015 01:25:45 +0100 Subject: [PATCH 2/5] Restrictions on moves out-from and into fixed-length arrays. No longer legal: `fn foo(a: [D; 5]) { drop(a); a[2] = D::new(); }`; one must first initialize the entirety of `a` before assigning to its individual elements. No longer legal: `fn foo(arr: [D; 5]) -> D { arr[2] }`, unless `D` implements `Copy`. This "move out-from" restriction only affects `expr[i]`, and not destructuring (e.g. `f([a, b, c]: Array) { ... }`). uses mem_categorization to distinguish destructuring-bind from array indexing. See discussion on RFC PR 533. [breaking-change] --- src/librustc/middle/mem_categorization.rs | 77 ++++++++++++++----- src/librustc_borrowck/borrowck/check_loans.rs | 10 ++- src/librustc_borrowck/borrowck/fragments.rs | 15 ++-- .../borrowck/gather_loans/gather_moves.rs | 11 ++- .../borrowck/gather_loans/move_error.rs | 15 +++- .../borrowck/gather_loans/restrictions.rs | 4 +- src/librustc_borrowck/borrowck/mod.rs | 41 +++++++++- src/librustc_borrowck/borrowck/move_data.rs | 9 ++- 8 files changed, 142 insertions(+), 40 deletions(-) diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 3e7aa0ff1d04e..58492c817fd0b 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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; + // 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 { +fn deref_kind(t: Ty, context: DerefKindContext) -> McResult { match t.sty { ty::ty_uniq(_) => { Ok(deref_ptr(Unique)) @@ -220,7 +228,12 @@ pub fn deref_kind(t: Ty) -> McResult { } 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(&self, node: &N, base_cmt: cmt<'tcx>, - deref_cnt: uint) + deref_cnt: uint, + deref_context: DerefKindContext) -> McResult> { 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> { - 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(&self, elt: &N, - mut base_cmt: cmt<'tcx>) + mut base_cmt: cmt<'tcx>, + context: InteriorOffsetKind) -> McResult> { //! 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(&self, elt: &N, - base_cmt: cmt<'tcx>) + base_cmt: cmt<'tcx>, + context: InteriorOffsetKind) -> McResult> { - 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(), } } } diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index b8d736ab9ca67..91f1121deaab1 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -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); } } } diff --git a/src/librustc_borrowck/borrowck/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs index ecbf3a4366d4a..bee1ada28e314 100644 --- a/src/librustc_borrowck/borrowck/fragments.rs +++ b/src/librustc_borrowck/borrowck/fragments.rs @@ -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), diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs index ca1fba53de42a..da53e9fac1187 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs @@ -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> { @@ -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) } diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index d9e86bd4b6d68..b00973c82bd97 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -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>) } } +// (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) => { diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs index ac1e097be6ff2..7078b2b5f1797 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs @@ -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(..) => { diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index e86388a59349f..0bad55948822f 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -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>> { 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_{}({}, {:?}, {:?}-{:?}, {})", diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_borrowck/borrowck/move_data.rs index 76c431fa4c58f..6607b5cac9c1d 100644 --- a/src/librustc_borrowck/borrowck/move_data.rs +++ b/src/librustc_borrowck/borrowck/move_data.rs @@ -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, _) | From ce341f79b49f2b141da608cf53c6b34c11a6d29a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Feb 2015 17:44:16 +0100 Subject: [PATCH 3/5] Add tests of move-out-of-array restriction. --- src/test/compile-fail/move-out-of-array-1.rs | 28 ++++++++++++++++ src/test/run-pass/copy-out-of-array-1.rs | 28 ++++++++++++++++ src/test/run-pass/destructure-array-1.rs | 35 ++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 src/test/compile-fail/move-out-of-array-1.rs create mode 100644 src/test/run-pass/copy-out-of-array-1.rs create mode 100644 src/test/run-pass/destructure-array-1.rs 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"), + } +} From 128ac9dfcbb096c8bbc373f554a0c97ca3e9e030 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 3 Feb 2015 18:10:56 +0100 Subject: [PATCH 4/5] Add tests of move-into-dead-array restriction. --- .../compile-fail/move-into-dead-array-1.rs | 25 +++++++++++++++++++ .../compile-fail/move-into-dead-array-2.rs | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/test/compile-fail/move-into-dead-array-1.rs create mode 100644 src/test/compile-fail/move-into-dead-array-2.rs diff --git a/src/test/compile-fail/move-into-dead-array-1.rs b/src/test/compile-fail/move-into-dead-array-1.rs new file mode 100644 index 0000000000000..61ccb694fd212 --- /dev/null +++ b/src/test/compile-fail/move-into-dead-array-1.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(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` +} From 4583272bf5054e84c4c59ba3b9334a52cfcf5208 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 4 Feb 2015 12:25:25 +0100 Subject: [PATCH 5/5] Updates to tests reflecting array-move restrictions. Note that the change to the error message in borrowck-use-in-index-lvalue.rs, where we 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). ---- drive-by: revise compile-fail/borrowck-vec-pattern-move-tail to make it really clear that there is a conflict that must be signaled. (A hypothetical future version of Rust might be able to accept the prior version of the code, since the previously updated index was not actually aliased.) --- .../borrowck-array-double-move.rs | 24 --------- .../borrowck-use-in-index-lvalue.rs | 4 +- .../borrowck-vec-pattern-move-tail.rs | 4 +- src/test/compile-fail/move-fragments-9.rs | 53 ------------------- 4 files changed, 5 insertions(+), 80 deletions(-) delete mode 100644 src/test/compile-fail/borrowck-array-double-move.rs diff --git a/src/test/compile-fail/borrowck-array-double-move.rs b/src/test/compile-fail/borrowck-array-double-move.rs deleted file mode 100644 index 3fb42b38e842c..0000000000000 --- a/src/test/compile-fail/borrowck-array-double-move.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(unknown_features)] -#![feature(box_syntax)] - -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[..]` -} - -fn main() { - f(); -} - 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/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs index 5f58027af5337..242a38440034c 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs @@ -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]; } 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() { }