Skip to content

Commit 0fefc23

Browse files
committed
Auto merge of #151103 - Zalathar:array-pat-len, r=<try>
mir_build: Simplify length-determination and indexing for array/slice patterns try-job: x86_64-gnu-llvm-21-3
2 parents 86a49fd + a72083f commit 0fefc23

File tree

2 files changed

+67
-51
lines changed

2 files changed

+67
-51
lines changed

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,25 +1221,32 @@ pub enum ProjectionElem<V, T> {
12211221
/// thing is true of the `ConstantIndex` and `Subslice` projections below.
12221222
Index(V),
12231223

1224-
/// These indices are generated by slice patterns. Easiest to explain
1225-
/// by example:
1224+
/// These endpoint-relative indices are generated by slice/array patterns.
1225+
///
1226+
/// For array types, `offset` is always relative to the start of the array.
1227+
/// For slice types, `from_end` determines whether `offset` is relative to
1228+
/// the start or the end of the slice being inspected.
1229+
///
1230+
/// Slice-pattern indices are easiest to explain by the position of `X` in
1231+
/// these examples:
12261232
///
12271233
/// ```ignore (illustrative)
1228-
/// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
1229-
/// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
1230-
/// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
1231-
/// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
1234+
/// [X, _, .., _, _] => { offset: 0, min_length: 4, from_end: false },
1235+
/// [_, X, .., _, _] => { offset: 1, min_length: 4, from_end: false },
1236+
/// [_, _, .., X, _] => { offset: 2, min_length: 4, from_end: true },
1237+
/// [_, _, .., _, X] => { offset: 1, min_length: 4, from_end: true },
12321238
/// ```
12331239
ConstantIndex {
1234-
/// index or -index (in Python terms), depending on from_end
1240+
/// - If `from_end == false`, this is a 0-based offset from the start of the array/slice.
1241+
/// - If `from_end == true`, this is a 1-based offset from the end of the slice.
12351242
offset: u64,
12361243
/// The thing being indexed must be at least this long -- otherwise, the
12371244
/// projection is UB.
12381245
///
12391246
/// For arrays this is always the exact length.
12401247
min_length: u64,
1241-
/// Counting backwards from end? This is always false when indexing an
1242-
/// array.
1248+
/// If `true`, `offset` is a 1-based offset from the end of the slice.
1249+
/// Always false when indexing an array.
12431250
from_end: bool,
12441251
},
12451252

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,58 +40,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4040
match_pairs: &mut Vec<MatchPairTree<'tcx>>,
4141
extra_data: &mut PatternExtraData<'tcx>,
4242
place: &PlaceBuilder<'tcx>,
43+
array_len: Option<u64>,
4344
prefix: &[Pat<'tcx>],
4445
opt_slice: &Option<Box<Pat<'tcx>>>,
4546
suffix: &[Pat<'tcx>],
4647
) {
47-
let tcx = self.tcx;
48-
let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
49-
let place_ty = place_resolved.ty(&self.local_decls, tcx).ty;
50-
match place_ty.kind() {
51-
ty::Array(_, length) => {
52-
if let Some(length) = length.try_to_target_usize(tcx) {
53-
(length, true)
54-
} else {
55-
// This can happen when the array length is a generic const
56-
// expression that couldn't be evaluated (e.g., due to an error).
57-
// Since there's already a compilation error, we use a fallback
58-
// to avoid an ICE.
59-
tcx.dcx().span_delayed_bug(
60-
tcx.def_span(self.def_id),
61-
"array length in pattern couldn't be evaluated",
62-
);
63-
((prefix.len() + suffix.len()).try_into().unwrap(), false)
64-
}
65-
}
66-
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
67-
}
68-
} else {
69-
((prefix.len() + suffix.len()).try_into().unwrap(), false)
48+
let prefix_len = u64::try_from(prefix.len()).unwrap();
49+
let suffix_len = u64::try_from(suffix.len()).unwrap();
50+
51+
// For slice patterns with a `..` followed by 0 or more suffix subpatterns,
52+
// the actual slice index of those subpatterns isn't statically known, so
53+
// we have to index them relative to the end of the slice.
54+
//
55+
// For array patterns, all subpatterns are indexed relative to the start.
56+
let (min_length, is_array) = match array_len {
57+
Some(len) => (len, true),
58+
None => (prefix_len + suffix_len, false),
7059
};
7160

72-
for (idx, subpattern) in prefix.iter().enumerate() {
73-
let elem =
74-
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
61+
for (offset, subpattern) in (0u64..).zip(prefix) {
62+
let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
7563
let place = place.clone_project(elem);
7664
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
7765
}
7866

7967
if let Some(subslice_pat) = opt_slice {
80-
let suffix_len = suffix.len() as u64;
8168
let subslice = place.clone_project(PlaceElem::Subslice {
82-
from: prefix.len() as u64,
83-
to: if exact_size { min_length - suffix_len } else { suffix_len },
84-
from_end: !exact_size,
69+
from: prefix_len,
70+
to: if is_array { min_length - suffix_len } else { suffix_len },
71+
from_end: !is_array,
8572
});
8673
MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
8774
}
8875

89-
for (idx, subpattern) in suffix.iter().rev().enumerate() {
90-
let end_offset = (idx + 1) as u64;
76+
for (end_offset, subpattern) in (1u64..).zip(suffix.iter().rev()) {
9177
let elem = ProjectionElem::ConstantIndex {
92-
offset: if exact_size { min_length - end_offset } else { end_offset },
78+
offset: if is_array { min_length - end_offset } else { end_offset },
9379
min_length,
94-
from_end: !exact_size,
80+
from_end: !is_array,
9581
};
9682
let place = place.clone_project(elem);
9783
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
@@ -256,21 +242,44 @@ impl<'tcx> MatchPairTree<'tcx> {
256242
}
257243

258244
PatKind::Array { ref prefix, ref slice, ref suffix } => {
259-
cx.prefix_slice_suffix(
260-
&mut subpairs,
261-
extra_data,
262-
&place_builder,
263-
prefix,
264-
slice,
265-
suffix,
266-
);
245+
// Determine the statically-known length of the array type being matched.
246+
// This should always succeed for legal programs, but could fail for
247+
// erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
248+
// so take care not to ICE if this fails.
249+
let array_len = match pattern.ty.kind() {
250+
ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
251+
_ => None,
252+
};
253+
if let Some(array_len) = array_len {
254+
cx.prefix_slice_suffix(
255+
&mut subpairs,
256+
extra_data,
257+
&place_builder,
258+
Some(array_len),
259+
prefix,
260+
slice,
261+
suffix,
262+
);
263+
} else {
264+
// If the array length couldn't be determined, ignore the
265+
// subpatterns and delayed-assert that compilation will fail.
266+
cx.tcx.dcx().span_delayed_bug(
267+
pattern.span,
268+
format!(
269+
"array length in pattern couldn't be determined for ty={:?}",
270+
pattern.ty
271+
),
272+
);
273+
}
274+
267275
None
268276
}
269277
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
270278
cx.prefix_slice_suffix(
271279
&mut subpairs,
272280
extra_data,
273281
&place_builder,
282+
None,
274283
prefix,
275284
slice,
276285
suffix,

0 commit comments

Comments
 (0)