Make const index and subslice array projections more useful

* `min_length` is now exact for const index elements.
* const index elements are always from the start.
* make array `Subslice` `PlaceElems` count both `from` and `to` from the
  start.
This commit is contained in:
Matthew Jasper 2019-11-22 20:28:02 +00:00
parent 7de9402b77
commit bf278ebd9d
11 changed files with 119 additions and 63 deletions

View file

@ -1714,18 +1714,25 @@ pub enum ProjectionElem<V, T> {
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
}

View file

@ -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)
}

View file

@ -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: _ } |

View file

@ -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))));
}

View file

@ -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),
},
),

View file

@ -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, _)

View file

@ -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)

View file

@ -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 }

View file

@ -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<Self, (Self, Vec<(Place<'tcx>, 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.

View file

@ -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)?,
})
}

View file

@ -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<Self::Path> {
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,
})
}