From 414e00d3509a4cfedaab0d9c712a5f41861ea0c6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 14 Jan 2026 15:55:49 +1100 Subject: [PATCH 1/3] Clarify the docs/examples for `ProjectionElem::ConstantIndex` --- compiler/rustc_middle/src/mir/syntax.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 83e9a1f1784b5..6ec874fb15f71 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1221,25 +1221,32 @@ pub enum ProjectionElem { /// 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, }, From a9ce7503d51b2be5a71a25315b4b4cb63eebf8b6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 14 Jan 2026 14:53:57 +1100 Subject: [PATCH 2/3] Pull array length determination out of `prefix_slice_suffix` --- .../src/builder/matches/match_pair.rs | 85 +++++++++++-------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 8cee3ff27e8f9..19b4cd70c0b9d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -40,33 +40,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match_pairs: &mut Vec>, extra_data: &mut PatternExtraData<'tcx>, place: &PlaceBuilder<'tcx>, + array_len: Option, prefix: &[Pat<'tcx>], opt_slice: &Option>>, 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() { @@ -77,11 +66,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } 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); } @@ -89,9 +77,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for (idx, subpattern) in suffix.iter().rev().enumerate() { let end_offset = (idx + 1) as u64; 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) @@ -256,14 +244,36 @@ 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 } => { @@ -271,6 +281,7 @@ impl<'tcx> MatchPairTree<'tcx> { &mut subpairs, extra_data, &place_builder, + None, prefix, slice, suffix, From a72083f739c369cd1ce675a1d278505656674af6 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 14 Jan 2026 16:13:49 +1100 Subject: [PATCH 3/3] Avoid some more usize-to-u64 casts in `prefix_slice_suffix` --- .../rustc_mir_build/src/builder/matches/match_pair.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 19b4cd70c0b9d..3edd0234b0ad7 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -58,9 +58,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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) } @@ -74,8 +73,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 is_array { min_length - end_offset } else { end_offset }, min_length,