From bf278ebd9d82e15aba014034520e18120a1d6211 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 22 Nov 2019 20:28:02 +0000 Subject: [PATCH 1/4] 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. --- src/librustc/mir/mod.rs | 24 +++++++---- src/librustc/mir/tcx.rs | 9 ++-- src/librustc/mir/visit.rs | 2 +- src/librustc_codegen_ssa/mir/place.rs | 3 +- .../borrow_check/nll/type_check/mod.rs | 23 ++++------ .../borrow_check/places_conflict.rs | 42 +++++++++++++++---- src/librustc_mir/build/matches/util.rs | 31 ++++++++++---- .../dataflow/move_paths/abs_domain.rs | 4 +- src/librustc_mir/dataflow/move_paths/mod.rs | 5 ++- src/librustc_mir/interpret/place.rs | 14 +++++-- src/librustc_mir/transform/elaborate_drops.rs | 7 ++-- 11 files changed, 110 insertions(+), 54 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index a7f5a22692515..afa6d8db443b7 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 a66a49f103f68..445fa6ea8cab3 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 703e0cc78c207..5d273fe85b6d2 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 e60b8861faf85..5e13cabced000 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 8d4e76cadbfc2..c16471dca7217 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 87a431a7fb80d..f0420a2359784 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, 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, .. }) + | (ProjectionElem::Subslice { to, .. }, 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, _) diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index aec9e6e57d46c..ec8b3c5e24bf2 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 d97f3b7417286..0665c0fb72c46 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 b599f4799446d..89ef9b245ce8f 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 a600eb11e1d03..42fbfeca3f05d 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 9970752a37698..8cc8883b9019f 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, }) } From 96dc03bad73cd18e7f58d90c2fc0198e618851e5 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 22 Nov 2019 22:03:25 +0000 Subject: [PATCH 2/4] Remove `uniform_array_move_out` passes These passes were buggy, MIR building is now responsible for canonicalizing `ConstantIndex` projections and `MoveData` is responsible for splitting `Subslice` projections. --- .../diagnostics/conflict_errors.rs | 5 +- src/librustc_mir/borrow_check/mod.rs | 67 ++- .../borrow_check/places_conflict.rs | 4 +- .../dataflow/move_paths/builder.rs | 131 ++++-- src/librustc_mir/transform/elaborate_drops.rs | 2 +- src/librustc_mir/transform/mod.rs | 3 - src/librustc_mir/transform/rustc_peek.rs | 2 +- .../transform/uniform_array_move_out.rs | 381 ------------------ src/test/mir-opt/const_prop/return_place.rs | 6 - src/test/mir-opt/uniform_array_move_out.rs | 56 +-- 10 files changed, 168 insertions(+), 489 deletions(-) delete mode 100644 src/librustc_mir/transform/uniform_array_move_out.rs diff --git a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs index a0f126fb2cb30..73310dfcc4559 100644 --- a/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs @@ -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!( diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 427003f24cb14..0cec19394a7f1 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -174,7 +174,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut errors_buffer = Vec::new(); let (move_data, move_errors): (MoveData<'tcx>, Option, 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)), }; @@ -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) => { @@ -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, @@ -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. // diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs index f0420a2359784..9245064f87594 100644 --- a/src/librustc_mir/borrow_check/places_conflict.rs +++ b/src/librustc_mir/borrow_check/places_conflict.rs @@ -533,8 +533,8 @@ fn place_projection_conflict<'tcx>( } } (ProjectionElem::ConstantIndex { offset, min_length: _, from_end: true }, - ProjectionElem::Subslice { to, .. }) - | (ProjectionElem::Subslice { 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: \ diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index 52016d4c9363a..fa0864e0de760 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -4,7 +4,7 @@ use rustc::ty::{self, TyCtxt}; use rustc_index::vec::IndexVec; use smallvec::{smallvec, SmallVec}; -use std::collections::hash_map::Entry; +use std::convert::TryInto; use std::mem; use super::abs_domain::Lift; @@ -17,12 +17,13 @@ use super::{ struct MoveDataBuilder<'a, 'tcx> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, } impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); let mut init_path_map = IndexVec::new(); @@ -30,6 +31,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { MoveDataBuilder { body, tcx, + param_env, errors: Vec::new(), data: MoveData { moves: IndexVec::new(), @@ -148,42 +150,47 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { InteriorOfSliceOrArray { ty: place_ty, is_index: true }, )); } - _ => { - // FIXME: still badly broken - } + _ => {} }, _ => {} }; - let proj = &place.projection[..i+1]; - base = match self - .builder - .data - .rev_lookup - .projections - .entry((base, elem.lift())) - { - Entry::Occupied(ent) => *ent.get(), - Entry::Vacant(ent) => { - let path = MoveDataBuilder::new_move_path( - &mut self.builder.data.move_paths, - &mut self.builder.data.path_map, - &mut self.builder.data.init_path_map, - Some(base), - Place { - base: place.base.clone(), - projection: tcx.intern_place_elems(proj), - }, - ); - ent.insert(path); - path - } - }; + base = self.add_move_path(base, elem, |tcx| { + Place { + base: place.base.clone(), + projection: tcx.intern_place_elems(&place.projection[..i+1]), + } + }); } Ok(base) } + fn add_move_path( + &mut self, + base: MovePathIndex, + elem: &PlaceElem<'tcx>, + mk_place: impl FnOnce(TyCtxt<'tcx>) -> Place<'tcx>, + ) -> MovePathIndex { + let MoveDataBuilder { + data: MoveData { rev_lookup, move_paths, path_map, init_path_map, .. }, + tcx, + .. + } = self.builder; + *rev_lookup.projections + .entry((base, elem.lift())) + .or_insert_with(move || { + let path = MoveDataBuilder::new_move_path( + move_paths, + path_map, + init_path_map, + Some(base), + mk_place(*tcx), + ); + path + }) + } + fn create_move_path(&mut self, place: &Place<'tcx>) { // This is an non-moving access (such as an overwrite or // drop), so this not being a valid move path is OK. @@ -214,8 +221,9 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { pub(super) fn gather_moves<'tcx>( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, ) -> Result, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)> { - let mut builder = MoveDataBuilder::new(body, tcx); + let mut builder = MoveDataBuilder::new(body, tcx, param_env); builder.gather_args(); @@ -411,20 +419,67 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_move(&mut self, place: &Place<'tcx>) { debug!("gather_move({:?}, {:?})", self.loc, place); - let path = match self.move_path_for(place) { - Ok(path) | Err(MoveError::UnionMove { path }) => path, - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((place.clone(), error)); - return; + if let [ + ref base @ .., + ProjectionElem::Subslice { from, to, from_end: false }, + ] = **place.projection { + // Split `Subslice` patterns into the corresponding list of + // `ConstIndex` patterns. This is done to ensure that all move paths + // are disjoint, which is expected by drop elaboration. + let base_place = Place { + base: place.base.clone(), + projection: self.builder.tcx.intern_place_elems(base), + }; + let base_path = match self.move_path_for(&base_place) { + Ok(path) => path, + Err(MoveError::UnionMove { path }) => { + self.record_move(place, path); + return; + } + Err(error @ MoveError::IllegalMove { .. }) => { + self.builder.errors.push((base_place, error)); + return; + } + }; + let base_ty = base_place.ty(self.builder.body, self.builder.tcx).ty; + let len: u32 = match base_ty.kind { + ty::Array(_, size) => { + let length = size.eval_usize(self.builder.tcx, self.builder.param_env); + length.try_into().expect( + "slice pattern of array with more than u32::MAX elements" + ) + } + _ => bug!("from_end: false slice pattern of non-array type"), + }; + for offset in from..to { + let elem = ProjectionElem::ConstantIndex { + offset, + min_length: len, + from_end: false, + }; + let path = self.add_move_path( + base_path, + &elem, + |tcx| tcx.mk_place_elem(base_place.clone(), elem), + ); + self.record_move(place, path); } - }; - let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc }); + } else { + match self.move_path_for(place) { + Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path), + Err(error @ MoveError::IllegalMove { .. }) => { + self.builder.errors.push((place.clone(), error)); + } + }; + } + } + fn record_move(&mut self, place: &Place<'tcx>, path: MovePathIndex) { + let move_out = self.builder.data.moves.push(MoveOut { path: path, source: self.loc }); debug!( "gather_move({:?}, {:?}): adding move {:?} of {:?}", self.loc, place, move_out, path ); - self.builder.data.path_map[path].push(move_out); self.builder.data.loc_map[self.loc].push(move_out); } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 8cc8883b9019f..1cacf1f3b0a57 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -26,7 +26,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { let def_id = src.def_id(); let param_env = tcx.param_env(src.def_id()).with_reveal_all(); - let move_data = match MoveData::gather_moves(body, tcx) { + let move_data = match MoveData::gather_moves(body, tcx, param_env) { Ok(move_data) => move_data, Err(_) => bug!("No `move_errors` should be allowed in MIR borrowck"), }; diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index bedf2a95c026e..2e1a08a022472 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -35,7 +35,6 @@ pub mod copy_prop; pub mod const_prop; pub mod generator; pub mod inline; -pub mod uniform_array_move_out; pub mod uninhabited_enum_branching; pub(crate) fn provide(providers: &mut Providers<'_>) { @@ -229,7 +228,6 @@ fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal> { // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, - &uniform_array_move_out::UniformArrayMoveOut, ]); body.ensure_predecessors(); tcx.alloc_steal_mir(body) @@ -294,7 +292,6 @@ fn run_optimization_passes<'tcx>( // Optimizations begin. &uninhabited_enum_branching::UninhabitedEnumBranching, &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"), - &uniform_array_move_out::RestoreSubsliceArrayMoveOut::new(tcx), &inline::Inline, // Lowering generator control-flow and variables diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs index 2a81e97b8ff25..4345fc66bb95e 100644 --- a/src/librustc_mir/transform/rustc_peek.rs +++ b/src/librustc_mir/transform/rustc_peek.rs @@ -37,7 +37,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { let attributes = tcx.get_attrs(def_id); let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx).unwrap(); + let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let dead_unwinds = BitSet::new_empty(body.basic_blocks().len()); let flow_inits = diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs deleted file mode 100644 index 71dd405386aa7..0000000000000 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ /dev/null @@ -1,381 +0,0 @@ -// This pass converts move out from array by Subslice and -// ConstIndex{.., from_end: true} to ConstIndex move out(s) from begin -// of array. It allows detect error by mir borrowck and elaborate -// drops for array without additional work. -// -// Example: -// -// let a = [ box 1,box 2, box 3]; -// if b { -// let [_a.., _] = a; -// } else { -// let [.., _b] = a; -// } -// -// mir statement _10 = move _2[:-1]; replaced by: -// StorageLive(_12); -// _12 = move _2[0 of 3]; -// StorageLive(_13); -// _13 = move _2[1 of 3]; -// _10 = [move _12, move _13] -// StorageDead(_12); -// StorageDead(_13); -// -// and mir statement _11 = move _2[-1 of 1]; replaced by: -// _11 = move _2[2 of 3]; -// -// FIXME: integrate this transformation to the mir build - -use rustc::ty; -use rustc::ty::TyCtxt; -use rustc::mir::*; -use rustc::mir::visit::{Visitor, PlaceContext, NonUseContext}; -use rustc_index::vec::{IndexVec}; -use crate::transform::{MirPass, MirSource}; -use crate::util::patch::MirPatch; - -pub struct UniformArrayMoveOut; - -impl<'tcx> MirPass<'tcx> for UniformArrayMoveOut { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { - let mut patch = MirPatch::new(body); - let param_env = tcx.param_env(src.def_id()); - { - let read_only_cache = read_only!(body); - let mut visitor - = UniformArrayMoveOutVisitor{ body, patch: &mut patch, tcx, param_env}; - visitor.visit_body(read_only_cache); - } - patch.apply(body); - } -} - -struct UniformArrayMoveOutVisitor<'a, 'tcx> { - body: &'a Body<'tcx>, - patch: &'a mut MirPatch<'tcx>, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> { - fn visit_assign(&mut self, - dst_place: &Place<'tcx>, - rvalue: &Rvalue<'tcx>, - location: Location) { - if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue { - if let &[ref proj_base @ .., elem] = src_place.projection.as_ref() { - if let ProjectionElem::ConstantIndex{offset: _, - min_length: _, - from_end: false} = elem { - // no need to transformation - } else { - let place_ty = - Place::ty_from(&src_place.base, proj_base, self.body, self.tcx).ty; - if let ty::Array(item_ty, const_size) = place_ty.kind { - if let Some(size) = const_size.try_eval_usize(self.tcx, self.param_env) { - assert!(size <= u32::max_value() as u64, - "uniform array move out doesn't supported - for array bigger then u32"); - self.uniform( - location, - dst_place, - &src_place.base, - &src_place.projection, - item_ty, - size as u32, - ); - } - } - - } - } - } - self.super_assign(dst_place, rvalue, location) - } -} - -impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { - fn uniform(&mut self, - location: Location, - dst_place: &Place<'tcx>, - base: &PlaceBase<'tcx>, - proj: &[PlaceElem<'tcx>], - item_ty: &'tcx ty::TyS<'tcx>, - size: u32) { - if let [proj_base @ .., elem] = proj { - match elem { - // uniforms statements like_10 = move _2[:-1]; - ProjectionElem::Subslice{from, to} => { - self.patch.make_nop(location); - let temps : Vec<_> = (*from..(size-*to)).map(|i| { - let temp = - self.patch.new_temp(item_ty, self.body.source_info(location).span); - self.patch.add_statement(location, StatementKind::StorageLive(temp)); - - let mut projection = proj_base.to_vec(); - projection.push(ProjectionElem::ConstantIndex { - offset: i, - min_length: size, - from_end: false, - }); - self.patch.add_assign( - location, - Place::from(temp), - Rvalue::Use(Operand::Move(Place { - base: base.clone(), - projection: self.tcx.intern_place_elems(&projection), - })), - ); - temp - }).collect(); - self.patch.add_assign( - location, - dst_place.clone(), - Rvalue::Aggregate( - box AggregateKind::Array(item_ty), - temps.iter().map( - |x| Operand::Move(Place::from(*x)) - ).collect() - ) - ); - for temp in temps { - self.patch.add_statement(location, StatementKind::StorageDead(temp)); - } - } - // uniforms statements like _11 = move _2[-1 of 1]; - ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => { - self.patch.make_nop(location); - - let mut projection = proj_base.to_vec(); - projection.push(ProjectionElem::ConstantIndex { - offset: size - offset, - min_length: size, - from_end: false, - }); - self.patch.add_assign( - location, - dst_place.clone(), - Rvalue::Use(Operand::Move(Place { - base: base.clone(), - projection: self.tcx.intern_place_elems(&projection), - })), - ); - } - _ => {} - } - } - } -} - -// Restore Subslice move out after analysis -// Example: -// -// next statements: -// StorageLive(_12); -// _12 = move _2[0 of 3]; -// StorageLive(_13); -// _13 = move _2[1 of 3]; -// _10 = [move _12, move _13] -// StorageDead(_12); -// StorageDead(_13); -// -// replaced by _10 = move _2[:-1]; - -pub struct RestoreSubsliceArrayMoveOut<'tcx> { - tcx: TyCtxt<'tcx> -} - -impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) { - let mut patch = MirPatch::new(body); - let param_env = tcx.param_env(src.def_id()); - { - let read_only_cache = read_only!(body); - let mut visitor = RestoreDataCollector { - locals_use: IndexVec::from_elem(LocalUse::new(), &body.local_decls), - candidates: vec![], - }; - visitor.visit_body(read_only_cache); - - for candidate in &visitor.candidates { - let statement = &body[candidate.block].statements[candidate.statement_index]; - if let StatementKind::Assign(box(ref dst_place, ref rval)) = statement.kind { - if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval { - let items : Vec<_> = items.iter().map(|item| { - if let Operand::Move(place) = item { - if let Some(local) = place.as_local() { - let local_use = &visitor.locals_use[local]; - let opt_index_and_place = - Self::try_get_item_source(local_use, body); - // each local should be used twice: - // in assign and in aggregate statements - if local_use.use_count == 2 && opt_index_and_place.is_some() { - let (index, src_place) = opt_index_and_place.unwrap(); - return Some((local_use, index, src_place)); - } - } - } - None - }).collect(); - - let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2); - let opt_size = opt_src_place.and_then(|src_place| { - let src_ty = Place::ty_from( - src_place.base, - src_place.projection, - &**body, - tcx - ).ty; - if let ty::Array(_, ref size_o) = src_ty.kind { - size_o.try_eval_usize(tcx, param_env) - } else { - None - } - }); - let restore_subslice = RestoreSubsliceArrayMoveOut { tcx }; - restore_subslice - .check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place); - } - } - } - } - patch.apply(body); - } -} - -impl RestoreSubsliceArrayMoveOut<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - RestoreSubsliceArrayMoveOut { tcx } - } - - // Checks that source has size, all locals are inited from same source place and - // indices is an integer interval. If all checks pass do the replacent. - // items are Vec> - fn check_and_patch(&self, - candidate: Location, - items: &[Option<(&LocalUse, u32, PlaceRef<'_, 'tcx>)>], - opt_size: Option, - patch: &mut MirPatch<'tcx>, - dst_place: &Place<'tcx>) { - let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2); - - if opt_size.is_some() && items.iter().all( - |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) { - let src_place = opt_src_place.unwrap(); - - let indices: Vec<_> = items.iter().map(|x| x.unwrap().1).collect(); - for i in 1..indices.len() { - if indices[i - 1] + 1 != indices[i] { - return; - } - } - - let min = *indices.first().unwrap(); - let max = *indices.last().unwrap(); - - for item in items { - let locals_use = item.unwrap().0; - patch.make_nop(locals_use.alive.unwrap()); - patch.make_nop(locals_use.dead.unwrap()); - patch.make_nop(locals_use.first_use.unwrap()); - } - patch.make_nop(candidate); - let size = opt_size.unwrap() as u32; - - let mut projection = src_place.projection.to_vec(); - projection.push(ProjectionElem::Subslice { from: min, to: size - max - 1 }); - patch.add_assign( - candidate, - dst_place.clone(), - Rvalue::Use(Operand::Move(Place { - base: src_place.base.clone(), - projection: self.tcx.intern_place_elems(&projection), - })), - ); - } - } - - fn try_get_item_source<'a>(local_use: &LocalUse, - body: &'a Body<'tcx>) -> Option<(u32, PlaceRef<'a, 'tcx>)> { - if let Some(location) = local_use.first_use { - let block = &body[location.block]; - if block.statements.len() > location.statement_index { - let statement = &block.statements[location.statement_index]; - if let StatementKind::Assign( - box(place, Rvalue::Use(Operand::Move(src_place))) - ) = &statement.kind { - if let (Some(_), PlaceRef { - base: _, - projection: &[.., ProjectionElem::ConstantIndex { - offset, min_length: _, from_end: false - }], - }) = (place.as_local(), src_place.as_ref()) { - if let StatementKind::Assign( - box(_, Rvalue::Use(Operand::Move(place))) - ) = &statement.kind { - if let PlaceRef { - base, - projection: &[ref proj_base @ .., _], - } = place.as_ref() { - return Some((offset, PlaceRef { - base, - projection: proj_base, - })) - } - } - } - } - } - } - None - } -} - -#[derive(Copy, Clone, Debug)] -struct LocalUse { - alive: Option, - dead: Option, - use_count: u32, - first_use: Option, -} - -impl LocalUse { - pub fn new() -> Self { - LocalUse{alive: None, dead: None, use_count: 0, first_use: None} - } -} - -struct RestoreDataCollector { - locals_use: IndexVec, - candidates: Vec, -} - -impl<'tcx> Visitor<'tcx> for RestoreDataCollector { - fn visit_assign(&mut self, - place: &Place<'tcx>, - rvalue: &Rvalue<'tcx>, - location: Location) { - if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue { - self.candidates.push(location); - } - self.super_assign(place, rvalue, location) - } - - fn visit_local(&mut self, - local: &Local, - context: PlaceContext, - location: Location) { - let local_use = &mut self.locals_use[*local]; - match context { - PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location), - PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location), - PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {} - _ => { - local_use.use_count += 1; - if local_use.first_use.is_none() { - local_use.first_use = Some(location); - } - } - } - } -} diff --git a/src/test/mir-opt/const_prop/return_place.rs b/src/test/mir-opt/const_prop/return_place.rs index cc9951b554dce..ea7c1e7ccd0cb 100644 --- a/src/test/mir-opt/const_prop/return_place.rs +++ b/src/test/mir-opt/const_prop/return_place.rs @@ -21,9 +21,6 @@ fn main() { // _0 = move (_1.0: u32); // return; // } -// bb2 (cleanup): { -// resume; -// } // } // END rustc.add.ConstProp.before.mir // START rustc.add.ConstProp.after.mir @@ -38,9 +35,6 @@ fn main() { // _0 = const 4u32; // return; // } -// bb2 (cleanup): { -// resume; -// } // } // END rustc.add.ConstProp.after.mir // START rustc.add.PreCodegen.before.mir diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs index c249154c71e23..f2e1864096ea9 100644 --- a/src/test/mir-opt/uniform_array_move_out.rs +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -18,58 +18,12 @@ fn main() { // END RUST SOURCE -// START rustc.move_out_from_end.UniformArrayMoveOut.before.mir -// StorageLive(_6); -// _6 = move _1[-1 of 1]; -// _0 = (); -// END rustc.move_out_from_end.UniformArrayMoveOut.before.mir - -// START rustc.move_out_from_end.UniformArrayMoveOut.after.mir -// StorageLive(_6); +// START rustc.move_out_from_end.mir_map.0.mir // _6 = move _1[1 of 2]; -// nop; // _0 = (); -// END rustc.move_out_from_end.UniformArrayMoveOut.after.mir - -// START rustc.move_out_by_subslice.UniformArrayMoveOut.before.mir -// StorageLive(_6); -// _6 = move _1[0:]; -// END rustc.move_out_by_subslice.UniformArrayMoveOut.before.mir - -// START rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir -// StorageLive(_6); -// StorageLive(_7); -// _7 = move _1[0 of 2]; -// StorageLive(_8); -// _8 = move _1[1 of 2]; -// _6 = [move _7, move _8]; -// StorageDead(_7); -// StorageDead(_8); -// nop; -// _0 = (); -// END rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir - -// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir -// StorageLive(_6); -// StorageLive(_7); -// _7 = move _1[0 of 2]; -// StorageLive(_8); -// _8 = move _1[1 of 2]; -// _6 = [move _7, move _8]; -// StorageDead(_7); -// StorageDead(_8); -// _0 = (); -// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir +// END rustc.move_out_from_end.mir_map.0.mir -// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir -// StorageLive(_6); -// nop; -// nop; -// nop; -// nop; -// _6 = move _1[0:]; -// nop; -// nop; -// nop; +// START rustc.move_out_by_subslice.mir_map.0.mir +// _6 = move _1[0..2]; // _0 = (); -// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir +// END rustc.move_out_by_subslice.mir_map.0.mir From cab7af986c8662d68213b9577c251036b4cbcb71 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 22 Nov 2019 22:08:03 +0000 Subject: [PATCH 3/4] Check for `ConstantIndex` move paths correctly in borrowck --- src/librustc_mir/borrow_check/mod.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 0cec19394a7f1..b4f2e2377ac7d 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -1738,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 { + fn move_path_for_place(&mut self, place: PlaceRef<'_, 'tcx>) -> Option { // 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). From d96485d49e3745a9b9f4b2ed6ba9cebf265f142e Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 22 Nov 2019 23:15:11 +0000 Subject: [PATCH 4/4] Add more tests for borrowck and dropck slice pattern handling --- ...borrowck-move-out-from-array-no-overlap.rs | 69 +++++++++ ...owck-move-out-from-array-use-no-overlap.rs | 69 +++++++++ .../borrowck-move-out-from-array-use.rs | 99 ++++++++++++ .../borrowck-move-out-from-array-use.stderr | 143 ++++++++++++++++++ .../borrowck/borrowck-move-out-from-array.rs | 69 ++++++++- .../borrowck-move-out-from-array.stderr | 100 ++++++++++-- ...e-pattern-element-loan-array-no-overlap.rs | 66 ++++++++ ...rrowck-slice-pattern-element-loan-array.rs | 60 ++++++++ ...ck-slice-pattern-element-loan-array.stderr | 86 +++++++++++ ...e-pattern-element-loan-slice-no-overlap.rs | 61 ++++++++ ...rowck-slice-pattern-element-loan-slice.rs} | 46 ------ ...k-slice-pattern-element-loan-slice.stderr} | 24 ++- src/test/ui/drop/dynamic-drop.rs | 32 ++++ 13 files changed, 849 insertions(+), 75 deletions(-) create mode 100644 src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap.rs create mode 100644 src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap.rs create mode 100644 src/test/ui/borrowck/borrowck-move-out-from-array-use.rs create mode 100644 src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr create mode 100644 src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array-no-overlap.rs create mode 100644 src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.rs create mode 100644 src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.stderr create mode 100644 src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice-no-overlap.rs rename src/test/ui/borrowck/{borrowck-slice-pattern-element-loan.rs => borrowck-slice-pattern-element-loan-slice.rs} (65%) rename src/test/ui/borrowck/{borrowck-slice-pattern-element-loan.stderr => borrowck-slice-pattern-element-loan-slice.stderr} (89%) diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap.rs b/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap.rs new file mode 100644 index 0000000000000..8f274cf73cb0e --- /dev/null +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-no-overlap.rs @@ -0,0 +1,69 @@ +// check-pass + +#![feature(slice_patterns)] + +fn array() -> [(String, String); 3] { + Default::default() +} + +// Const Index + Const Index + +fn move_out_from_begin_and_one_from_end() { + let a = array(); + let [_, _, _x] = a; + let [.., _y, _] = a; +} + +fn move_out_from_begin_field_and_end_field() { + let a = array(); + let [_, _, (_x, _)] = a; + let [.., (_, _y)] = a; +} + +// Const Index + Slice + +fn move_out_by_const_index_and_subslice() { + let a = array(); + let [_x, _, _] = a; + let [_, _y @ ..] = a; +} + +fn move_out_by_const_index_end_and_subslice() { + let a = array(); + let [.., _x] = a; + let [_y @ .., _] = a; +} + +fn move_out_by_const_index_field_and_subslice() { + let a = array(); + let [(_x, _), _, _] = a; + let [_, _y @ ..] = a; +} + +fn move_out_by_const_index_end_field_and_subslice() { + let a = array(); + let [.., (_x, _)] = a; + let [_y @ .., _] = a; +} + +fn move_out_by_const_subslice_and_index_field() { + let a = array(); + let [_, _y @ ..] = a; + let [(_x, _), _, _] = a; +} + +fn move_out_by_const_subslice_and_end_index_field() { + let a = array(); + let [_y @ .., _] = a; + let [.., (_x, _)] = a; +} + +// Slice + Slice + +fn move_out_by_subslice_and_subslice() { + let a = array(); + let [x @ .., _, _] = a; + let [_, _y @ ..] = a; +} + +fn main() {} diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap.rs b/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap.rs new file mode 100644 index 0000000000000..57ce2417570b0 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use-no-overlap.rs @@ -0,0 +1,69 @@ +// check-pass + +#![feature(slice_patterns)] + +fn array() -> [(String, String); 3] { + Default::default() +} + +// Const Index + Const Index + +fn move_out_from_begin_and_one_from_end() { + let a = array(); + let [_, _, _x] = a; + let [.., ref _y, _] = a; +} + +fn move_out_from_begin_field_and_end_field() { + let a = array(); + let [_, _, (_x, _)] = a; + let [.., (_, ref _y)] = a; +} + +// Const Index + Slice + +fn move_out_by_const_index_and_subslice() { + let a = array(); + let [_x, _, _] = a; + let [_, ref _y @ ..] = a; +} + +fn move_out_by_const_index_end_and_subslice() { + let a = array(); + let [.., _x] = a; + let [ref _y @ .., _] = a; +} + +fn move_out_by_const_index_field_and_subslice() { + let a = array(); + let [(_x, _), _, _] = a; + let [_, ref _y @ ..] = a; +} + +fn move_out_by_const_index_end_field_and_subslice() { + let a = array(); + let [.., (_x, _)] = a; + let [ref _y @ .., _] = a; +} + +fn move_out_by_const_subslice_and_index_field() { + let a = array(); + let [_, _y @ ..] = a; + let [(ref _x, _), _, _] = a; +} + +fn move_out_by_const_subslice_and_end_index_field() { + let a = array(); + let [_y @ .., _] = a; + let [.., (ref _x, _)] = a; +} + +// Slice + Slice + +fn move_out_by_subslice_and_subslice() { + let a = array(); + let [x @ .., _, _] = a; + let [_, ref _y @ ..] = a; +} + +fn main() {} diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use.rs b/src/test/ui/borrowck/borrowck-move-out-from-array-use.rs new file mode 100644 index 0000000000000..778beefbf2c85 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use.rs @@ -0,0 +1,99 @@ +#![feature(slice_patterns)] + +fn array() -> [(String, String); 3] { + Default::default() +} + +// Const Index + Const Index + +fn move_out_from_begin_and_end() { + let a = array(); + let [_, _, _x] = a; + let [.., ref _y] = a; //~ ERROR [E0382] +} + +fn move_out_from_begin_field_and_end() { + let a = array(); + let [_, _, (_x, _)] = a; + let [.., ref _y] = a; //~ ERROR [E0382] +} + +fn move_out_from_begin_field_and_end_field() { + let a = array(); + let [_, _, (_x, _)] = a; + let [.., (ref _y, _)] = a; //~ ERROR [E0382] +} + +// Const Index + Slice + +fn move_out_by_const_index_and_subslice() { + let a = array(); + let [_x, _, _] = a; + let [ref _y @ .., _, _] = a; //~ ERROR [E0382] +} + +fn move_out_by_const_index_end_and_subslice() { + let a = array(); + let [.., _x] = a; + let [_, _, ref _y @ ..] = a; //~ ERROR [E0382] +} + +fn move_out_by_const_index_field_and_subslice() { + let a = array(); + let [(_x, _), _, _] = a; + let [ref _y @ .., _, _] = a; //~ ERROR [E0382] +} + +fn move_out_by_const_index_end_field_and_subslice() { + let a = array(); + let [.., (_x, _)] = a; + let [_, _, ref _y @ ..] = a; //~ ERROR [E0382] +} + +fn move_out_by_subslice_and_const_index_field() { + let a = array(); + let [_y @ .., _, _] = a; + let [(ref _x, _), _, _] = a; //~ ERROR [E0382] +} + +fn move_out_by_subslice_and_const_index_end_field() { + let a = array(); + let [_, _, _y @ ..] = a; + let [.., (ref _x, _)] = a; //~ ERROR [E0382] +} + +// Slice + Slice + +fn move_out_by_subslice_and_subslice() { + let a = array(); + let [x @ .., _] = a; + let [_, ref _y @ ..] = a; //~ ERROR [E0382] +} + +// Move + Assign + +fn move_out_and_assign_end() { + let mut a = array(); + let [_, _, _x] = a; + a[2] = Default::default(); //~ ERROR [E0382] +} + +fn move_out_and_assign_end_field() { + let mut a = array(); + let [_, _, (_x, _)] = a; + a[2].1 = Default::default(); //~ ERROR [E0382] +} + +fn move_out_slice_and_assign_end() { + let mut a = array(); + let [_, _, _x @ ..] = a; + a[0] = Default::default(); //~ ERROR [E0382] +} + +fn move_out_slice_and_assign_end_field() { + let mut a = array(); + let [_, _, _x @ ..] = a; + a[0].1 = Default::default(); //~ ERROR [E0382] +} + +fn main() {} diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr new file mode 100644 index 0000000000000..2a7b89132c1b7 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-move-out-from-array-use.stderr @@ -0,0 +1,143 @@ +error[E0382]: borrow of moved value: `a[..]` + --> $DIR/borrowck-move-out-from-array-use.rs:12:14 + | +LL | let [_, _, _x] = a; + | -- value moved here +LL | let [.., ref _y] = a; + | ^^^^^^ value borrowed here after move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a[..]` + --> $DIR/borrowck-move-out-from-array-use.rs:18:14 + | +LL | let [_, _, (_x, _)] = a; + | -- value moved here +LL | let [.., ref _y] = a; + | ^^^^^^ value borrowed here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a[..].0` + --> $DIR/borrowck-move-out-from-array-use.rs:24:15 + | +LL | let [_, _, (_x, _)] = a; + | -- value moved here +LL | let [.., (ref _y, _)] = a; + | ^^^^^^ value borrowed here after move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:32:10 + | +LL | let [_x, _, _] = a; + | -- value moved here +LL | let [ref _y @ .., _, _] = a; + | ^^^^^^^^^^^ value borrowed here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:38:16 + | +LL | let [.., _x] = a; + | -- value moved here +LL | let [_, _, ref _y @ ..] = a; + | ^^^^^^^^^^^ value borrowed here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:44:10 + | +LL | let [(_x, _), _, _] = a; + | -- value moved here +LL | let [ref _y @ .., _, _] = a; + | ^^^^^^^^^^^ value borrowed here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:50:16 + | +LL | let [.., (_x, _)] = a; + | -- value moved here +LL | let [_, _, ref _y @ ..] = a; + | ^^^^^^^^^^^ value borrowed here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a[..]` + --> $DIR/borrowck-move-out-from-array-use.rs:56:11 + | +LL | let [_y @ .., _, _] = a; + | ------- value moved here +LL | let [(ref _x, _), _, _] = a; + | ^^^^^^ value borrowed here after move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a[..]` + --> $DIR/borrowck-move-out-from-array-use.rs:62:15 + | +LL | let [_, _, _y @ ..] = a; + | ------- value moved here +LL | let [.., (ref _x, _)] = a; + | ^^^^^^ value borrowed here after move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:70:13 + | +LL | let [x @ .., _] = a; + | ------ value moved here +LL | let [_, ref _y @ ..] = a; + | ^^^^^^^^^^^ value borrowed here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:78:5 + | +LL | let [_, _, _x] = a; + | -- value moved here +LL | a[2] = Default::default(); + | ^^^^ value used here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:84:5 + | +LL | let [_, _, (_x, _)] = a; + | -- value moved here +LL | a[2].1 = Default::default(); + | ^^^^ value used here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:90:5 + | +LL | let [_, _, _x @ ..] = a; + | ------- value moved here +LL | a[0] = Default::default(); + | ^^^^ value used here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array-use.rs:96:5 + | +LL | let [_, _, _x @ ..] = a; + | ------- value moved here +LL | a[0].1 = Default::default(); + | ^^^^ value used here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array.rs b/src/test/ui/borrowck/borrowck-move-out-from-array.rs index ee6abf407a304..f9d3f6f2c0724 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array.rs +++ b/src/test/ui/borrowck/borrowck-move-out-from-array.rs @@ -1,16 +1,73 @@ -#![feature(box_syntax)] #![feature(slice_patterns)] +fn array() -> [(String, String); 3] { + Default::default() +} + +// Const Index + Const Index + fn move_out_from_begin_and_end() { - let a = [box 1, box 2]; - let [_, _x] = a; + let a = array(); + let [_, _, _x] = a; + let [.., _y] = a; //~ ERROR [E0382] +} + +fn move_out_from_begin_field_and_end() { + let a = array(); + let [_, _, (_x, _)] = a; let [.., _y] = a; //~ ERROR [E0382] } +fn move_out_from_begin_field_and_end_field() { + let a = array(); + let [_, _, (_x, _)] = a; + let [.., (_y, _)] = a; //~ ERROR [E0382] +} + +// Const Index + Slice + fn move_out_by_const_index_and_subslice() { - let a = [box 1, box 2]; - let [_x, _] = a; - let [_y @ ..] = a; //~ ERROR [E0382] + let a = array(); + let [_x, _, _] = a; + let [_y @ .., _, _] = a; //~ ERROR [E0382] +} + +fn move_out_by_const_index_end_and_subslice() { + let a = array(); + let [.., _x] = a; + let [_, _, _y @ ..] = a; //~ ERROR [E0382] +} + +fn move_out_by_const_index_field_and_subslice() { + let a = array(); + let [(_x, _), _, _] = a; + let [_y @ .., _, _] = a; //~ ERROR [E0382] +} + +fn move_out_by_const_index_end_field_and_subslice() { + let a = array(); + let [.., (_x, _)] = a; + let [_, _, _y @ ..] = a; //~ ERROR [E0382] +} + +fn move_out_by_subslice_and_const_index_field() { + let a = array(); + let [_y @ .., _, _] = a; + let [(_x, _), _, _] = a; //~ ERROR [E0382] +} + +fn move_out_by_subslice_and_const_index_end_field() { + let a = array(); + let [_, _, _y @ ..] = a; + let [.., (_x, _)] = a; //~ ERROR [E0382] +} + +// Slice + Slice + +fn move_out_by_subslice_and_subslice() { + let a = array(); + let [x @ .., _] = a; + let [_, _y @ ..] = a; //~ ERROR [E0382] } fn main() {} diff --git a/src/test/ui/borrowck/borrowck-move-out-from-array.stderr b/src/test/ui/borrowck/borrowck-move-out-from-array.stderr index b34c03e6deff8..08134a2a323e7 100644 --- a/src/test/ui/borrowck/borrowck-move-out-from-array.stderr +++ b/src/test/ui/borrowck/borrowck-move-out-from-array.stderr @@ -1,23 +1,103 @@ error[E0382]: use of moved value: `a[..]` - --> $DIR/borrowck-move-out-from-array.rs:7:14 + --> $DIR/borrowck-move-out-from-array.rs:12:14 | -LL | let [_, _x] = a; - | -- value moved here +LL | let [_, _, _x] = a; + | -- value moved here LL | let [.., _y] = a; | ^^ value used here after move | - = note: move occurs because `a[..]` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait error[E0382]: use of moved value: `a[..]` - --> $DIR/borrowck-move-out-from-array.rs:13:10 + --> $DIR/borrowck-move-out-from-array.rs:18:14 | -LL | let [_x, _] = a; +LL | let [_, _, (_x, _)] = a; + | -- value moved here +LL | let [.., _y] = a; + | ^^ value used here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a[..].0` + --> $DIR/borrowck-move-out-from-array.rs:24:15 + | +LL | let [_, _, (_x, _)] = a; + | -- value moved here +LL | let [.., (_y, _)] = a; + | ^^ value used here after move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array.rs:32:10 + | +LL | let [_x, _, _] = a; | -- value moved here -LL | let [_y @ ..] = a; - | ^^^^^^^ value used here after move +LL | let [_y @ .., _, _] = a; + | ^^^^^^^ value used here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array.rs:38:16 + | +LL | let [.., _x] = a; + | -- value moved here +LL | let [_, _, _y @ ..] = a; + | ^^^^^^^ value used here after partial move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array.rs:44:10 + | +LL | let [(_x, _), _, _] = a; + | -- value moved here +LL | let [_y @ .., _, _] = a; + | ^^^^^^^ value used here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array.rs:50:16 + | +LL | let [.., (_x, _)] = a; + | -- value moved here +LL | let [_, _, _y @ ..] = a; + | ^^^^^^^ value used here after partial move + | + = note: move occurs because `a[..].0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a[..].0` + --> $DIR/borrowck-move-out-from-array.rs:56:11 + | +LL | let [_y @ .., _, _] = a; + | ------- value moved here +LL | let [(_x, _), _, _] = a; + | ^^ value used here after move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a[..].0` + --> $DIR/borrowck-move-out-from-array.rs:62:15 + | +LL | let [_, _, _y @ ..] = a; + | ------- value moved here +LL | let [.., (_x, _)] = a; + | ^^ value used here after move + | + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `a` + --> $DIR/borrowck-move-out-from-array.rs:70:13 + | +LL | let [x @ .., _] = a; + | ------ value moved here +LL | let [_, _y @ ..] = a; + | ^^^^^^^ value used here after partial move | - = note: move occurs because `a[..]` has type `std::boxed::Box`, which does not implement the `Copy` trait + = note: move occurs because `a[..]` has type `(std::string::String, std::string::String)`, which does not implement the `Copy` trait -error: aborting due to 2 previous errors +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array-no-overlap.rs b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array-no-overlap.rs new file mode 100644 index 0000000000000..7d91a21264723 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array-no-overlap.rs @@ -0,0 +1,66 @@ +// check-pass + +#![feature(slice_patterns)] + +fn nop(_s: &[& i32]) {} +fn nop_subslice(_s: &[i32]) {} + +fn const_index_ok(s: &mut [i32; 4]) { + let [ref first, ref second, _, ref fourth, ..] = *s; + let [_, _, ref mut third, ..] = *s; + nop(&[first, second, third, fourth]); +} + +fn const_index_from_end_ok(s: &mut [i32; 4]) { + let [.., ref fourth, ref third, _, ref first] = *s; + let [.., ref mut second, _] = *s; + nop(&[first, second, third, fourth]); +} + +fn const_index_mixed(s: &mut [i32; 6]) { + let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s; + + let [ref mut from_begin0, ..] = *s; + nop(&[from_begin0, from_end1, from_end3, from_end4]); + let [_, ref mut from_begin1, ..] = *s; + nop(&[from_begin1, from_end1, from_end3, from_end4]); + + let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s; + + let [.., ref mut from_end1] = *s; + nop(&[from_begin0, from_begin1, from_begin3, from_end1]); + let [.., ref mut from_end2, _] = *s; + nop(&[from_begin0, from_begin1, from_begin3, from_end2]); + let [.., ref mut from_end4, _, _, _] = *s; + nop(&[from_begin0, from_begin1, from_begin3, from_end4]); +} + +fn const_index_and_subslice_ok(s: &mut [i32; 4]) { + let [ref first, ref second, ..] = *s; + let [_, _, ref mut tail @ ..] = *s; + nop(&[first, second]); + nop_subslice(tail); +} + +fn const_index_and_subslice_from_end_ok(s: &mut [i32; 4]) { + let [.., ref second, ref first] = *s; + let [ref mut tail @ .., _, _] = *s; + nop(&[first, second]); + nop_subslice(tail); +} + +fn subslices(s: &mut [i32; 4]) { + let [_, _, ref s1 @ ..] = *s; + let [ref mut s2 @ .., _, _] = *s; + nop_subslice(s1); + nop_subslice(s2); +} + +fn main() { + let mut v = [1,2,3,4]; + const_index_ok(&mut v); + const_index_from_end_ok(&mut v); + const_index_and_subslice_ok(&mut v); + const_index_and_subslice_from_end_ok(&mut v); + subslices(&mut v); +} diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.rs b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.rs new file mode 100644 index 0000000000000..f03a2ab8fa8e4 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.rs @@ -0,0 +1,60 @@ +#![feature(slice_patterns)] + +fn nop(_s: &[& i32]) {} +fn nop_subslice(_s: &[i32]) {} + +fn const_index_err(s: &mut [i32; 4]) { + let [ref first, ref second, ..] = *s; + let [_, ref mut second2, ref mut third, ..] = *s; //~ERROR + nop(&[first, second, second2, third]); +} + +fn const_index_from_end_err(s: &mut [i32; 4]) { + let [.., ref fourth, ref third, _, ref first] = *s; + let [.., ref mut third2, _, _] = *s; //~ERROR + nop(&[first, third, third2, fourth]); +} + +fn const_index_mixed(s: &mut [i32; 6]) { + let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s; + + let [_, _, ref mut from_begin2, ..] = *s; //~ERROR + nop(&[from_begin2, from_end1, from_end3, from_end4]); + let [_, _, _, ref mut from_begin3, ..] = *s; //~ERROR + nop(&[from_begin3, from_end1, from_end3, from_end4]); + + let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s; + + let [.., ref mut from_end3, _, _] = *s; //~ERROR + nop(&[from_begin0, from_begin1, from_begin3, from_end3]); +} + +fn const_index_and_subslice_err(s: &mut [i32; 4]) { + let [ref first, ref second, ..] = *s; + let [_, ref mut tail @ ..] = *s; //~ERROR + nop(&[first, second]); + nop_subslice(tail); +} + +fn const_index_and_subslice_from_end_err(s: &mut [i32; 4]) { + let [.., ref second, ref first] = *s; + let [ref mut tail @ .., _] = *s; //~ERROR + nop(&[first, second]); + nop_subslice(tail); +} + +fn subslices_overlap(s: &mut [i32; 4]) { + let [_, ref s1 @ ..] = *s; + let [ref mut s2 @ .., _, _] = *s; //~ERROR + nop_subslice(s1); + nop_subslice(s2); +} + +fn main() { + let mut v = [1,2,3,4]; + const_index_err(&mut v); + const_index_from_end_err(&mut v); + const_index_and_subslice_err(&mut v); + const_index_and_subslice_from_end_err(&mut v); + subslices_overlap(&mut v); +} diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.stderr b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.stderr new file mode 100644 index 0000000000000..e50e7eb3e2230 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-array.stderr @@ -0,0 +1,86 @@ +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:8:13 + | +LL | let [ref first, ref second, ..] = *s; + | ---------- immutable borrow occurs here +LL | let [_, ref mut second2, ref mut third, ..] = *s; + | ^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[first, second, second2, third]); + | ------ immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:14:14 + | +LL | let [.., ref fourth, ref third, _, ref first] = *s; + | --------- immutable borrow occurs here +LL | let [.., ref mut third2, _, _] = *s; + | ^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[first, third, third2, fourth]); + | ----- immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:21:16 + | +LL | let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s; + | ------------- immutable borrow occurs here +LL | +LL | let [_, _, ref mut from_begin2, ..] = *s; + | ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[from_begin2, from_end1, from_end3, from_end4]); + | --------- immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:23:19 + | +LL | let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s; + | ------------- immutable borrow occurs here +... +LL | let [_, _, _, ref mut from_begin3, ..] = *s; + | ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[from_begin3, from_end1, from_end3, from_end4]); + | --------- immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:28:14 + | +LL | let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s; + | --------------- immutable borrow occurs here +LL | +LL | let [.., ref mut from_end3, _, _] = *s; + | ^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[from_begin0, from_begin1, from_begin3, from_end3]); + | ----------- immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:34:13 + | +LL | let [ref first, ref second, ..] = *s; + | ---------- immutable borrow occurs here +LL | let [_, ref mut tail @ ..] = *s; + | ^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[first, second]); + | ------ immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:41:10 + | +LL | let [.., ref second, ref first] = *s; + | ---------- immutable borrow occurs here +LL | let [ref mut tail @ .., _] = *s; + | ^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop(&[first, second]); + | ------ immutable borrow later used here + +error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable + --> $DIR/borrowck-slice-pattern-element-loan-array.rs:48:10 + | +LL | let [_, ref s1 @ ..] = *s; + | ----------- immutable borrow occurs here +LL | let [ref mut s2 @ .., _, _] = *s; + | ^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | nop_subslice(s1); + | -- immutable borrow later used here + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice-no-overlap.rs b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice-no-overlap.rs new file mode 100644 index 0000000000000..e69071f87720b --- /dev/null +++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice-no-overlap.rs @@ -0,0 +1,61 @@ +// check-pass + +#![feature(slice_patterns)] + +fn nop(_s: &[& i32]) {} +fn nop_subslice(_s: &[i32]) {} + +fn const_index_ok(s: &mut [i32]) { + if let [ref first, ref second, _, ref fourth, ..] = *s { + if let [_, _, ref mut third, ..] = *s { + nop(&[first, second, third, fourth]); + } + } +} + +fn const_index_from_end_ok(s: &mut [i32]) { + if let [.., ref fourth, ref third, _, ref first] = *s { + if let [.., ref mut second, _] = *s { + nop(&[first, second, third, fourth]); + } + } +} + +fn const_index_mixed(s: &mut [i32]) { + if let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s { + if let [ref mut from_begin0, ..] = *s { + nop(&[from_begin0, from_end1, from_end3, from_end4]); + } + } + if let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s { + if let [.., ref mut from_end1] = *s { + nop(&[from_begin0, from_begin1, from_begin3, from_end1]); + } + } +} + +fn const_index_and_subslice_ok(s: &mut [i32]) { + if let [ref first, ref second, ..] = *s { + if let [_, _, ref mut tail @ ..] = *s { + nop(&[first, second]); + nop_subslice(tail); + } + } +} + +fn const_index_and_subslice_from_end_ok(s: &mut [i32]) { + if let [.., ref second, ref first] = *s { + if let [ref mut tail @ .., _, _] = *s { + nop(&[first, second]); + nop_subslice(tail); + } + } +} + +fn main() { + let mut v = [1,2,3,4]; + const_index_ok(&mut v); + const_index_from_end_ok(&mut v); + const_index_and_subslice_ok(&mut v); + const_index_and_subslice_from_end_ok(&mut v); +} diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice.rs similarity index 65% rename from src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs rename to src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice.rs index a6b54f9537ddc..2ef98741dc35a 100644 --- a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.rs +++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice.rs @@ -1,18 +1,8 @@ -//compile-flags: -Z borrowck=mir - #![feature(slice_patterns)] fn nop(_s: &[& i32]) {} fn nop_subslice(_s: &[i32]) {} -fn const_index_ok(s: &mut [i32]) { - if let [ref first, ref second, _, ref fourth, ..] = *s { - if let [_, _, ref mut third, ..] = *s { - nop(&[first, second, third, fourth]); - } - } -} - fn const_index_err(s: &mut [i32]) { if let [ref first, ref second, ..] = *s { if let [_, ref mut second2, ref mut third, ..] = *s { //~ERROR @@ -21,14 +11,6 @@ fn const_index_err(s: &mut [i32]) { } } -fn const_index_from_end_ok(s: &mut [i32]) { - if let [.., ref fourth, ref third, _, ref first] = *s { - if let [.., ref mut second, _] = *s { - nop(&[first, second, third, fourth]); - } - } -} - fn const_index_from_end_err(s: &mut [i32]) { if let [.., ref fourth, ref third, _, ref first] = *s { if let [.., ref mut third2, _, _] = *s { //~ERROR @@ -39,9 +21,6 @@ fn const_index_from_end_err(s: &mut [i32]) { fn const_index_mixed(s: &mut [i32]) { if let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s { - if let [ref mut from_begin0, ..] = *s { - nop(&[from_begin0, from_end1, from_end3, from_end4]); - } if let [_, ref mut from_begin1, ..] = *s { //~ERROR nop(&[from_begin1, from_end1, from_end3, from_end4]); } @@ -53,9 +32,6 @@ fn const_index_mixed(s: &mut [i32]) { } } if let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s { - if let [.., ref mut from_end1] = *s { - nop(&[from_begin0, from_begin1, from_begin3, from_end1]); - } if let [.., ref mut from_end2, _] = *s { //~ERROR nop(&[from_begin0, from_begin1, from_begin3, from_end2]); } @@ -68,15 +44,6 @@ fn const_index_mixed(s: &mut [i32]) { } } -fn const_index_and_subslice_ok(s: &mut [i32]) { - if let [ref first, ref second, ..] = *s { - if let [_, _, ref mut tail @ ..] = *s { - nop(&[first, second]); - nop_subslice(tail); - } - } -} - fn const_index_and_subslice_err(s: &mut [i32]) { if let [ref first, ref second, ..] = *s { if let [_, ref mut tail @ ..] = *s { //~ERROR @@ -86,15 +53,6 @@ fn const_index_and_subslice_err(s: &mut [i32]) { } } -fn const_index_and_subslice_from_end_ok(s: &mut [i32]) { - if let [.., ref second, ref first] = *s { - if let [ref mut tail @ .., _, _] = *s { - nop(&[first, second]); - nop_subslice(tail); - } - } -} - fn const_index_and_subslice_from_end_err(s: &mut [i32]) { if let [.., ref second, ref first] = *s { if let [ref mut tail @ .., _] = *s { //~ERROR @@ -115,13 +73,9 @@ fn subslices(s: &mut [i32]) { fn main() { let mut v = [1,2,3,4]; - const_index_ok(&mut v); const_index_err(&mut v); - const_index_from_end_ok(&mut v); const_index_from_end_err(&mut v); - const_index_and_subslice_ok(&mut v); const_index_and_subslice_err(&mut v); - const_index_and_subslice_from_end_ok(&mut v); const_index_and_subslice_from_end_err(&mut v); subslices(&mut v); } diff --git a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice.stderr similarity index 89% rename from src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr rename to src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice.stderr index 2c019f4461182..b6f5ac64b2061 100644 --- a/src/test/ui/borrowck/borrowck-slice-pattern-element-loan.stderr +++ b/src/test/ui/borrowck/borrowck-slice-pattern-element-loan-slice.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:18:20 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:8:20 | LL | if let [ref first, ref second, ..] = *s { | ---------- immutable borrow occurs here @@ -9,7 +9,7 @@ LL | nop(&[first, second, second2, third]); | ------ immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:34:21 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:16:21 | LL | if let [.., ref fourth, ref third, _, ref first] = *s { | --------- immutable borrow occurs here @@ -19,18 +19,17 @@ LL | nop(&[first, third, third2, fourth]); | ----- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:45:20 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:24:20 | LL | if let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s { | ------------- immutable borrow occurs here -... LL | if let [_, ref mut from_begin1, ..] = *s { | ^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | nop(&[from_begin1, from_end1, from_end3, from_end4]); | --------- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:48:23 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:27:23 | LL | if let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s { | ------------- immutable borrow occurs here @@ -41,7 +40,7 @@ LL | nop(&[from_begin2, from_end1, from_end3, from_end4]); | --------- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:51:26 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:30:26 | LL | if let [.., _, ref from_end4, ref from_end3, _, ref from_end1] = *s { | ------------- immutable borrow occurs here @@ -52,18 +51,17 @@ LL | nop(&[from_begin3, from_end1, from_end3, from_end4]); | --------- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:59:21 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:35:21 | LL | if let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s { | --------------- immutable borrow occurs here -... LL | if let [.., ref mut from_end2, _] = *s { | ^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | nop(&[from_begin0, from_begin1, from_begin3, from_end2]); | ----------- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:62:21 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:38:21 | LL | if let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s { | --------------- immutable borrow occurs here @@ -74,7 +72,7 @@ LL | nop(&[from_begin0, from_begin1, from_begin3, from_end3]); | ----------- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:65:21 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:41:21 | LL | if let [ref from_begin0, ref from_begin1, _, ref from_begin3, _, ..] = *s { | --------------- immutable borrow occurs here @@ -85,7 +83,7 @@ LL | nop(&[from_begin0, from_begin1, from_begin3, from_end4]); | ----------- immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:82:20 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:49:20 | LL | if let [ref first, ref second, ..] = *s { | ---------- immutable borrow occurs here @@ -95,7 +93,7 @@ LL | nop(&[first, second]); | ------ immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:100:17 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:58:17 | LL | if let [.., ref second, ref first] = *s { | ---------- immutable borrow occurs here @@ -105,7 +103,7 @@ LL | nop(&[first, second]); | ------ immutable borrow later used here error[E0502]: cannot borrow `s[..]` as mutable because it is also borrowed as immutable - --> $DIR/borrowck-slice-pattern-element-loan.rs:109:17 + --> $DIR/borrowck-slice-pattern-element-loan-slice.rs:67:17 | LL | if let [_, _, _, ref s1 @ ..] = *s { | ----------- immutable borrow occurs here diff --git a/src/test/ui/drop/dynamic-drop.rs b/src/test/ui/drop/dynamic-drop.rs index 29dcbfe9609a0..0f0ec0ba460c8 100644 --- a/src/test/ui/drop/dynamic-drop.rs +++ b/src/test/ui/drop/dynamic-drop.rs @@ -269,6 +269,28 @@ fn subslice_pattern_reassign(a: &Allocator) { let[_, _y @ ..] = ar; } +fn index_field_mixed_ends(a: &Allocator) { + let ar = [(a.alloc(), a.alloc()), (a.alloc(), a.alloc())]; + let[(_x, _), ..] = ar; + let[(_, _y), _] = ar; + let[_, (_, _w)] = ar; + let[.., (_z, _)] = ar; +} + +fn subslice_mixed_min_lengths(a: &Allocator, c: i32) { + let ar = [(a.alloc(), a.alloc()), (a.alloc(), a.alloc())]; + match c { + 0 => { let[_x, ..] = ar; } + 1 => { let[_x, _, ..] = ar; } + 2 => { let[_x, _] = ar; } + 3 => { let[(_x, _), _, ..] = ar; } + 4 => { let[.., (_x, _)] = ar; } + 5 => { let[.., (_x, _), _] = ar; } + 6 => { let [_y @ ..] = ar; } + _ => { let [_y @ .., _] = ar; } + } +} + fn panic_after_return(a: &Allocator) -> Ptr<'_> { // Panic in the drop of `p` or `q` can leak let exceptions = vec![8, 9]; @@ -422,6 +444,16 @@ fn main() { run_test(|a| slice_pattern_reassign(a)); run_test(|a| subslice_pattern_reassign(a)); + run_test(|a| index_field_mixed_ends(a)); + run_test(|a| subslice_mixed_min_lengths(a, 0)); + run_test(|a| subslice_mixed_min_lengths(a, 1)); + run_test(|a| subslice_mixed_min_lengths(a, 2)); + run_test(|a| subslice_mixed_min_lengths(a, 3)); + run_test(|a| subslice_mixed_min_lengths(a, 4)); + run_test(|a| subslice_mixed_min_lengths(a, 5)); + run_test(|a| subslice_mixed_min_lengths(a, 6)); + run_test(|a| subslice_mixed_min_lengths(a, 7)); + run_test(|a| { panic_after_return(a); });