Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1221,25 +1221,32 @@ pub enum ProjectionElem<V, T> {
/// thing is true of the `ConstantIndex` and `Subslice` projections below.
Index(V),

/// These indices are generated by slice patterns. Easiest to explain
/// by example:
/// These endpoint-relative indices are generated by slice/array patterns.
///
/// For array types, `offset` is always relative to the start of the array.
/// For slice types, `from_end` determines whether `offset` is relative to
/// the start or the end of the slice being inspected.
///
/// Slice-pattern indices are easiest to explain by the position of `X` in
/// these examples:
///
/// ```ignore (illustrative)
/// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
/// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
/// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
/// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
/// [X, _, .., _, _] => { offset: 0, min_length: 4, from_end: false },
/// [_, X, .., _, _] => { offset: 1, min_length: 4, from_end: false },
/// [_, _, .., X, _] => { offset: 2, min_length: 4, from_end: true },
/// [_, _, .., _, X] => { offset: 1, min_length: 4, from_end: true },
/// ```
ConstantIndex {
/// index or -index (in Python terms), depending on from_end
/// - If `from_end == false`, this is a 0-based offset from the start of the array/slice.
/// - If `from_end == true`, this is a 1-based offset from the end of the slice.
offset: u64,
/// The thing being indexed must be at least this long -- otherwise, the
/// projection is UB.
///
/// For arrays this is always the exact length.
min_length: u64,
/// Counting backwards from end? This is always false when indexing an
/// array.
/// If `true`, `offset` is a 1-based offset from the end of the slice.
/// Always false when indexing an array.
from_end: bool,
},

Expand Down
93 changes: 51 additions & 42 deletions compiler/rustc_mir_build/src/builder/matches/match_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,58 +40,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match_pairs: &mut Vec<MatchPairTree<'tcx>>,
extra_data: &mut PatternExtraData<'tcx>,
place: &PlaceBuilder<'tcx>,
array_len: Option<u64>,
prefix: &[Pat<'tcx>],
opt_slice: &Option<Box<Pat<'tcx>>>,
suffix: &[Pat<'tcx>],
) {
let tcx = self.tcx;
let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
let place_ty = place_resolved.ty(&self.local_decls, tcx).ty;
match place_ty.kind() {
ty::Array(_, length) => {
if let Some(length) = length.try_to_target_usize(tcx) {
(length, true)
} else {
// This can happen when the array length is a generic const
// expression that couldn't be evaluated (e.g., due to an error).
// Since there's already a compilation error, we use a fallback
// to avoid an ICE.
tcx.dcx().span_delayed_bug(
tcx.def_span(self.def_id),
"array length in pattern couldn't be evaluated",
);
((prefix.len() + suffix.len()).try_into().unwrap(), false)
}
}
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
}
} else {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
let prefix_len = u64::try_from(prefix.len()).unwrap();
let suffix_len = u64::try_from(suffix.len()).unwrap();

// For slice patterns with a `..` followed by 0 or more suffix subpatterns,
// the actual slice index of those subpatterns isn't statically known, so
// we have to index them relative to the end of the slice.
//
// For array patterns, all subpatterns are indexed relative to the start.
let (min_length, is_array) = match array_len {
Some(len) => (len, true),
None => (prefix_len + suffix_len, false),
};

for (idx, subpattern) in prefix.iter().enumerate() {
let elem =
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
for (offset, subpattern) in (0u64..).zip(prefix) {
let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
}

if let Some(subslice_pat) = opt_slice {
let suffix_len = suffix.len() as u64;
let subslice = place.clone_project(PlaceElem::Subslice {
from: prefix.len() as u64,
to: if exact_size { min_length - suffix_len } else { suffix_len },
from_end: !exact_size,
from: prefix_len,
to: if is_array { min_length - suffix_len } else { suffix_len },
from_end: !is_array,
});
MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
}

for (idx, subpattern) in suffix.iter().rev().enumerate() {
let end_offset = (idx + 1) as u64;
for (end_offset, subpattern) in (1u64..).zip(suffix.iter().rev()) {
let elem = ProjectionElem::ConstantIndex {
offset: if exact_size { min_length - end_offset } else { end_offset },
offset: if is_array { min_length - end_offset } else { end_offset },
min_length,
from_end: !exact_size,
from_end: !is_array,
};
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
Expand Down Expand Up @@ -256,21 +242,44 @@ impl<'tcx> MatchPairTree<'tcx> {
}

PatKind::Array { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(
&mut subpairs,
extra_data,
&place_builder,
prefix,
slice,
suffix,
);
// Determine the statically-known length of the array type being matched.
// This should always succeed for legal programs, but could fail for
// erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
// so take care not to ICE if this fails.
let array_len = match pattern.ty.kind() {
ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
_ => None,
};
if let Some(array_len) = array_len {
cx.prefix_slice_suffix(
&mut subpairs,
extra_data,
&place_builder,
Some(array_len),
prefix,
slice,
suffix,
);
} else {
// If the array length couldn't be determined, ignore the
// subpatterns and delayed-assert that compilation will fail.
cx.tcx.dcx().span_delayed_bug(
pattern.span,
format!(
"array length in pattern couldn't be determined for ty={:?}",
pattern.ty
),
);
}

None
}
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(
&mut subpairs,
extra_data,
&place_builder,
None,
prefix,
slice,
suffix,
Expand Down
Loading