Skip to content

Commit

Permalink
Auto merge of #66650 - matthewjasper:nonuniform-array-move, r=pnkfelix
Browse files Browse the repository at this point in the history
Remove uniform array move MIR passes

This PR fixes a number of bugs caused by limitations of this pass

* Projections from constant indexes weren't being canonicalized
* Constant indexes from the start weren't being canonicalized (they could have different min_lengths)
* It didn't apply to non-moves

This PR makes the following changes to support removing this pass:

* ConstantIndex of arrays are now generated in a canonical form (from the start, min_length is the actual length).
* Subslices are now split when generating move paths and when checking subslices have been moved.

Additionally

* The parent move path of a projection from an array element is now calculated correctly

closes #66502
  • Loading branch information
bors committed Dec 11, 2019
2 parents 27d6f55 + d96485d commit de0abf7
Show file tree
Hide file tree
Showing 32 changed files with 1,131 additions and 631 deletions.
24 changes: 17 additions & 7 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)?;
}
}
}

Expand Down Expand Up @@ -2456,7 +2466,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
}

Expand Down
9 changes: 6 additions & 3 deletions src/librustc/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: _ } |
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_codegen_ssa/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,14 +565,15 @@ 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)
.projection_ty(tcx, elem).ty;
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))));
}
Expand Down
5 changes: 1 addition & 4 deletions src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
.collect();

if move_out_indices.is_empty() {
let root_place = self
.prefixes(used_place, PrefixSet::All)
.last()
.unwrap();
let root_place = PlaceRef { projection: &[], ..used_place };

if !self.uninitialized_error_reported.insert(root_place) {
debug!(
Expand Down
88 changes: 71 additions & 17 deletions src/librustc_mir/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fn do_mir_borrowck<'a, 'tcx>(

let mut errors_buffer = Vec::new();
let (move_data, move_errors): (MoveData<'tcx>, Option<Vec<(Place<'tcx>, MoveError<'tcx>)>>) =
match MoveData::gather_moves(&body, tcx) {
match MoveData::gather_moves(&body, tcx, param_env) {
Ok(move_data) => (move_data, None),
Err((move_data, move_errors)) => (move_data, Some(move_errors)),
};
Expand Down Expand Up @@ -1600,7 +1600,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(prefix, place_span.0, place_span.1),
mpi,
);
return; // don't bother finding other problems.
}
}
Err(NoMovePathFound::ReachedStatic) => {
Expand All @@ -1614,6 +1613,46 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}

/// Subslices correspond to multiple move paths, so we iterate through the
/// elements of the base array. For each element we check
///
/// * Does this element overlap with our slice.
/// * Is any part of it uninitialized.
fn check_if_subslice_element_is_moved(
&mut self,
location: Location,
desired_action: InitializationRequiringAction,
place_span: (PlaceRef<'cx, 'tcx>, Span),
maybe_uninits: &FlowAtLocation<'tcx, MaybeUninitializedPlaces<'cx, 'tcx>>,
from: u32,
to: u32,
) {
if let Some(mpi) = self.move_path_for_place(place_span.0) {
let mut child = self.move_data.move_paths[mpi].first_child;
while let Some(child_mpi) = child {
let child_move_place = &self.move_data.move_paths[child_mpi];
let child_place = &child_move_place.place;
let last_proj = child_place.projection.last().unwrap();
if let ProjectionElem::ConstantIndex { offset, from_end, .. } = last_proj {
debug_assert!(!from_end, "Array constant indexing shouldn't be `from_end`.");

if (from..to).contains(offset) {
if let Some(uninit_child) = maybe_uninits.has_any_child_of(child_mpi) {
self.report_use_of_moved_or_uninitialized(
location,
desired_action,
(place_span.0, place_span.0, place_span.1),
uninit_child,
);
return; // don't bother finding other problems.
}
}
}
child = child_move_place.next_sibling;
}
}
}

fn check_if_path_or_subpath_is_moved(
&mut self,
location: Location,
Expand All @@ -1640,6 +1679,30 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {

self.check_if_full_path_is_moved(location, desired_action, place_span, flow_state);

if let [
base_proj @ ..,
ProjectionElem::Subslice { from, to, from_end: false },
] = place_span.0.projection {
let place_ty = Place::ty_from(
place_span.0.base,
base_proj,
self.body(),
self.infcx.tcx,
);
if let ty::Array(..) = place_ty.ty.kind {
let array_place = PlaceRef { base: place_span.0.base, projection: base_proj };
self.check_if_subslice_element_is_moved(
location,
desired_action,
(array_place, place_span.1),
maybe_uninits,
*from,
*to,
);
return;
}
}

// A move of any shallow suffix of `place` also interferes
// with an attempt to use `place`. This is scenario 3 above.
//
Expand Down Expand Up @@ -1675,25 +1738,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// static variable, as we do not track those in the MoveData.
fn move_path_closest_to(
&mut self,
place: PlaceRef<'cx, 'tcx>,
place: PlaceRef<'_, 'tcx>,
) -> Result<(PlaceRef<'cx, 'tcx>, MovePathIndex), NoMovePathFound> {
let mut last_prefix = place.base;

for prefix in self.prefixes(place, PrefixSet::All) {
if let Some(mpi) = self.move_path_for_place(prefix) {
return Ok((prefix, mpi));
}

last_prefix = prefix.base;
}

match last_prefix {
PlaceBase::Local(_) => panic!("should have move path for every Local"),
PlaceBase::Static(_) => Err(NoMovePathFound::ReachedStatic),
match self.move_data.rev_lookup.find(place) {
LookupResult::Parent(Some(mpi))
| LookupResult::Exact(mpi) => Ok((self.move_data.move_paths[mpi].place.as_ref(), mpi)),
LookupResult::Parent(None) => Err(NoMovePathFound::ReachedStatic),
}
}

fn move_path_for_place(&mut self, place: PlaceRef<'cx, 'tcx>) -> Option<MovePathIndex> {
fn move_path_for_place(&mut self, place: PlaceRef<'_, 'tcx>) -> Option<MovePathIndex> {
// If returns None, then there is no move path corresponding
// to a direct owner of `place` (which means there is nothing
// that borrowck tracks for its analysis).
Expand Down
23 changes: 8 additions & 15 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -675,23 +675,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),
},
),
Expand Down
42 changes: 35 additions & 7 deletions src/librustc_mir/borrow_check/places_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,34 +503,62 @@ fn place_projection_conflict<'tcx>(
Overlap::Disjoint
}
}
(
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: 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");
"place_element_conflict: DISJOINT-OR-EQ-SLICE-CONSTANT-INDEX-SUBSLICE");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE");
debug!("place_element_conflict: DISJOINT-SLICE-CONSTANT-INDEX-SUBSLICE");
Overlap::Disjoint
}
}
(ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true },
ProjectionElem::Subslice {from: _, to })
| (ProjectionElem::Subslice {from: _, to },
ProjectionElem::Subslice { to, from_end: true, .. })
| (ProjectionElem::Subslice { to, from_end: true, .. },
ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }) => {
if offset > to {
debug!("place_element_conflict: \
DISJOINT-OR-EQ-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
DISJOINT-OR-EQ-SLICE-CONSTANT-INDEX-SUBSLICE-FE");
Overlap::EqualOrDisjoint
} else {
debug!("place_element_conflict: DISJOINT-ARRAY-CONSTANT-INDEX-SUBSLICE-FE");
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, _)
Expand Down
Loading

0 comments on commit de0abf7

Please sign in to comment.