Skip to content

Commit 73e6d62

Browse files
Rollup merge of #151103 - array-pat-len, r=Nadrieril,petrochenkov
mir_build: Simplify length-determination and indexing for array/slice patterns The existing length-determination code in `prefix_slice_suffix` has ended up overly complicated, partly because it doesn't know in advance whether the pattern is supposed to be an array pattern or a slice pattern. Pulling most of that step out into the `PatKind::Array` arm makes the whole thing a bit nicer overall. There should (hopefully) be no change to compiler output. The biggest “functional” change is that we now discard the subpatterns of an array pattern of unknowable length, instead of treating it as a slice pattern. I'm not aware of any way for this to make an observable difference, and it can only occur when compilation is already doomed to fail.
2 parents 0741782 + a72083f commit 73e6d62

2 files changed

Lines changed: 67 additions & 51 deletions

File tree

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)