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, _) | 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() 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() { } 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"), + } +}