diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index a7f5a2269251..afa6d8db443b 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1714,18 +1714,25 @@ pub enum ProjectionElem { ConstantIndex { /// index or -index (in Python terms), depending on from_end offset: u32, - /// thing being indexed must be at least this long + /// The thing being indexed must be at least this long. For arrays this + /// is always the exact length. min_length: u32, - /// counting backwards from end? + /// Counting backwards from end? This is always false when indexing an + /// array. from_end: bool, }, /// These indices are generated by slice patterns. /// - /// slice[from:-to] in Python terms. + /// If `from_end` is true `slice[from..slice.len() - to]`. + /// Otherwise `array[from..to]`. Subslice { from: u32, to: u32, + /// Whether `to` counts from the start or end of the array/slice. + /// For `PlaceElem`s this is `true` if and only if the base is a slice. + /// For `ProjectionKind`, this can also be `true` for arrays. + from_end: bool, }, /// "Downcast" to a variant of an ADT. Currently, we only introduce @@ -1914,15 +1921,18 @@ impl Debug for Place<'_> { ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; } - ProjectionElem::Subslice { from, to } if *to == 0 => { + ProjectionElem::Subslice { from, to, from_end: true } if *to == 0 => { write!(fmt, "[{:?}:]", from)?; } - ProjectionElem::Subslice { from, to } if *from == 0 => { + ProjectionElem::Subslice { from, to, from_end: true } if *from == 0 => { write!(fmt, "[:-{:?}]", to)?; } - ProjectionElem::Subslice { from, to } => { + ProjectionElem::Subslice { from, to, from_end: true } => { write!(fmt, "[{:?}:-{:?}]", from, to)?; } + ProjectionElem::Subslice { from, to, from_end: false } => { + write!(fmt, "[{:?}..{:?}]", from, to)?; + } } } @@ -2452,7 +2462,7 @@ impl UserTypeProjection { } pub(crate) fn subslice(mut self, from: u32, to: u32) -> Self { - self.projs.push(ProjectionElem::Subslice { from, to }); + self.projs.push(ProjectionElem::Subslice { from, to, from_end: true }); self } diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index a66a49f103f6..445fa6ea8cab 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -88,14 +88,17 @@ impl<'tcx> PlaceTy<'tcx> { } ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => PlaceTy::from_ty(self.ty.builtin_index().unwrap()), - ProjectionElem::Subslice { from, to } => { + ProjectionElem::Subslice { from, to, from_end } => { PlaceTy::from_ty(match self.ty.kind { - ty::Array(inner, size) => { + ty::Slice(..) => self.ty, + ty::Array(inner, _) if !from_end => { + tcx.mk_array(inner, (to - from) as u64) + } + ty::Array(inner, size) if from_end => { let size = size.eval_usize(tcx, param_env); let len = size - (from as u64) - (to as u64); tcx.mk_array(inner, len) } - ty::Slice(..) => self.ty, _ => { bug!("cannot subslice non-array type: `{:?}`", self) } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 703e0cc78c20..5d273fe85b6d 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -954,7 +954,7 @@ macro_rules! visit_place_fns { ); } ProjectionElem::Deref | - ProjectionElem::Subslice { from: _, to: _ } | + ProjectionElem::Subslice { from: _, to: _, from_end: _ } | ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } | diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index e60b8861faf8..5e13cabced00 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -565,7 +565,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let llindex = bx.sub(lllen, lloffset); cg_base.project_index(bx, llindex) } - mir::ProjectionElem::Subslice { from, to } => { + mir::ProjectionElem::Subslice { from, to, from_end } => { let mut subslice = cg_base.project_index(bx, bx.cx().const_usize(*from as u64)); let projected_ty = PlaceTy::from_ty(cg_base.layout.ty) @@ -573,6 +573,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { subslice.layout = bx.cx().layout_of(self.monomorphize(&projected_ty)); if subslice.layout.is_unsized() { + assert!(from_end, "slice subslices should be `from_end`"); subslice.llextra = Some(bx.sub(cg_base.llextra.unwrap(), bx.cx().const_usize((*from as u64) + (*to as u64)))); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 8d4e76cadbfc..c16471dca721 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -673,23 +673,16 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { }), ) } - ProjectionElem::Subslice { from, to } => PlaceTy::from_ty( + ProjectionElem::Subslice { from, to, from_end } => PlaceTy::from_ty( match base_ty.kind { - ty::Array(inner, size) => { - let size = size.eval_usize(tcx, self.cx.param_env); - let min_size = (from as u64) + (to as u64); - if let Some(rest_size) = size.checked_sub(min_size) { - tcx.mk_array(inner, rest_size) - } else { - span_mirbug_and_err!( - self, - place, - "taking too-small slice of {:?}", - base_ty - ) - } + ty::Array(inner, _) => { + assert!(!from_end, "array subslices should not use from_end"); + tcx.mk_array(inner, (to - from) as u64) } - ty::Slice(..) => base_ty, + ty::Slice(..) => { + assert!(from_end, "slice subslices should use from_end"); + base_ty + }, _ => span_mirbug_and_err!(self, place, "slice of non-array {:?}", base_ty), }, ), diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index 87a431a7fb80..f0420a235978 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -503,34 +503,62 @@ fn place_projection_conflict<'tcx>( Overlap::Disjoint } } - (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }, - ProjectionElem::Subslice {from, .. }) - | (ProjectionElem::Subslice {from, .. }, - ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => { - if offset >= from { - debug!( - "place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE"); + ( + ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }, + ProjectionElem::Subslice { from, to, from_end: false } + ) + | ( + ProjectionElem::Subslice { from, to, from_end: false }, + ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false } + ) => { + if (from..to).contains(&offset) { + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE"); Overlap::EqualOrDisjoint } else { debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE"); Overlap::Disjoint } } - (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }, - ProjectionElem::Subslice {from: _, to }) - | (ProjectionElem::Subslice {from: _, to }, - ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => { - if offset > to { - debug!("place_element_conflict: \ - DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE"); + (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }, + ProjectionElem::Subslice {from, .. }) + | (ProjectionElem::Subslice {from, .. }, + ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false }) => { + if offset >= from { + debug!( + "place_element_conflict: DISJOINT-OR-EQ-SLICE-CONSTANT-INDEX-SUBSLICE"); Overlap::EqualOrDisjoint } else { - debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE"); + debug!("place_element_conflict: DISJOINT-SLICE-CONSTANT-INDEX-SUBSLICE"); Overlap::Disjoint } } + (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }, + ProjectionElem::Subslice { to, .. }) + | (ProjectionElem::Subslice { to, .. }, + ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => { + if offset > to { + debug!("place_element_conflict: \ + DISJOINT-OR-EQ-SLICE-CONSTANT-INDEX-SUBSLICE-FE"); + Overlap::EqualOrDisjoint + } else { + debug!("place_element_conflict: DISJOINT-SLICE-CONSTANT-INDEX-SUBSLICE-FE"); + Overlap::Disjoint + } + } + ( + ProjectionElem::Subslice { from: f1, to: t1, from_end: false }, + ProjectionElem::Subslice { from: f2, to: t2, from_end: false } + ) => { + if f2 >= t1 || f1 >= t2 { + debug!("place_element_conflict: DISJOINT-ARRAY-SUBSLICES"); + Overlap::Disjoint + } else { + debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES"); + Overlap::EqualOrDisjoint + } + } (ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => { - debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-SUBSLICES"); + debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-SUBSLICES"); Overlap::EqualOrDisjoint } (ProjectionElem::Deref, _) diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index aec9e6e57d46..ec8b3c5e24bf 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -2,6 +2,7 @@ use crate::build::Builder; use crate::build::matches::MatchPair; use crate::hair::*; use rustc::mir::*; +use rustc::ty; use smallvec::SmallVec; use std::u32; use std::convert::TryInto; @@ -31,9 +32,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { prefix: &'pat [Pat<'tcx>], opt_slice: Option<&'pat Pat<'tcx>>, suffix: &'pat [Pat<'tcx>]) { - let min_length = prefix.len() + suffix.len(); - let min_length = min_length.try_into().unwrap(); let tcx = self.hir.tcx(); + let (min_length, exact_size) = match place.ty(&self.local_decls, tcx).ty.kind { + ty::Array(_, length) => ( + length.eval_usize(tcx, self.hir.param_env).try_into().unwrap(), + true + ), + _ => ( + (prefix.len() + suffix.len()).try_into().unwrap(), + false, + ), + }; match_pairs.extend( prefix.iter() @@ -50,10 +59,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); if let Some(subslice_pat) = opt_slice { - let subslice = tcx.mk_place_elem(place.clone(),ProjectionElem::Subslice { - from: prefix.len() as u32, - to: suffix.len() as u32 - }); + let suffix_len = suffix.len() as u32; + let subslice = tcx.mk_place_elem( + place.clone(), + ProjectionElem::Subslice { + from: prefix.len() as u32, + to: if exact_size { min_length - suffix_len } else { suffix_len }, + from_end: !exact_size, + }, + ); match_pairs.push(MatchPair::new(subslice, subslice_pat)); } @@ -62,10 +76,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .rev() .enumerate() .map(|(idx, subpattern)| { + let end_offset = (idx + 1) as u32; let elem = ProjectionElem::ConstantIndex { - offset: (idx+1) as u32, + offset: if exact_size { min_length - end_offset } else { end_offset }, min_length, - from_end: true, + from_end: !exact_size, }; let place = tcx.mk_place_elem(place.clone(), elem); MatchPair::new(place, subpattern) diff --git a/src/librustc_mir/dataflow/move_paths/abs_domain.rs b/src/librustc_mir/dataflow/move_paths/abs_domain.rs index d97f3b741728..0665c0fb72c4 100644 --- a/src/librustc_mir/dataflow/move_paths/abs_domain.rs +++ b/src/librustc_mir/dataflow/move_paths/abs_domain.rs @@ -49,8 +49,8 @@ impl<'tcx> Lift for PlaceElem<'tcx> { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Field(ref f, ty) => ProjectionElem::Field(f.clone(), ty.lift()), ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), - ProjectionElem::Subslice { from, to } => { - ProjectionElem::Subslice { from: from, to: to } + ProjectionElem::Subslice { from, to, from_end } => { + ProjectionElem::Subslice { from, to, from_end } } ProjectionElem::ConstantIndex { offset, min_length, from_end } => { ProjectionElem::ConstantIndex { offset, min_length, from_end } diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index b599f4799446..89ef9b245ce8 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -1,6 +1,6 @@ use core::slice::Iter; use rustc::mir::*; -use rustc::ty::{Ty, TyCtxt}; +use rustc::ty::{Ty, TyCtxt, ParamEnv}; use rustc::util::nodemap::FxHashMap; use rustc_index::vec::{Enumerated, Idx, IndexVec}; use smallvec::SmallVec; @@ -318,8 +318,9 @@ impl<'tcx> MoveData<'tcx> { pub fn gather_moves( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, ) -> Result, MoveError<'tcx>)>)> { - builder::gather_moves(body, tcx) + builder::gather_moves(body, tcx, param_env) } /// For the move path `mpi`, returns the root local variable (if any) that starts the path. diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index a600eb11e1d0..42fbfeca3f05 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -451,9 +451,15 @@ where base: MPlaceTy<'tcx, M::PointerTag>, from: u64, to: u64, + from_end: bool, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let len = base.len(self)?; // also asserts that we have a type where this makes sense - assert!(from <= len - to); + let actual_to = if from_end { + assert!(from <= len - to); + len - to + } else { + to + }; // Not using layout method because that works with usize, and does not work with slices // (that have count 0 in their layout). @@ -464,7 +470,7 @@ where }; // Compute meta and new layout - let inner_len = len - to - from; + let inner_len = actual_to - from; let (meta, ty) = match base.layout.ty.kind { // It is not nice to match on the type, but that seems to be the only way to // implement this. @@ -528,8 +534,8 @@ where self.mplace_field(base, index)? } - Subslice { from, to } => - self.mplace_subslice(base, u64::from(from), u64::from(to))?, + Subslice { from, to, from_end } => + self.mplace_subslice(base, u64::from(from), u64::from(to), from_end)?, }) } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 9970752a3769..8cc8883b9019 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -234,12 +234,11 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option { dataflow::move_path_children_matching(self.ctxt.move_data(), path, |e| match e { - ProjectionElem::ConstantIndex { offset, min_length: _, from_end: false } => { + ProjectionElem::ConstantIndex { offset, min_length, from_end } => { + debug_assert!(size == *min_length, "min_length should be exact for arrays"); + assert!(!from_end, "from_end should not be used for array element ConstantIndex"); *offset == index } - ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true } => { - size - offset == index - } _ => false, }) }