1use std::sync::Arc;
23use rustc_abi::FieldIdx;
4use rustc_middle::mir::{Pinnedness, Place, PlaceElem, ProjectionElem};
5use rustc_middle::span_bug;
6use rustc_middle::thir::{Ascription, DerefPatBorrowMode, FieldPat, Pat, PatKind};
7use rustc_middle::ty::{self, Ty, TypeVisitableExt};
8use rustc_span::Span;
910use crate::builder::Builder;
11use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
12use crate::builder::matches::{
13FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase,
14};
1516/// For an array or slice pattern's subpatterns (prefix/slice/suffix), returns a list
17/// of those subpatterns, each paired with a suitably-projected [`PlaceBuilder`].
18fn prefix_slice_suffix<'a, 'tcx>(
19 place: &PlaceBuilder<'tcx>,
20 array_len: Option<u64>, // Some for array patterns; None for slice patterns
21prefix: &'a [Pat<'tcx>],
22 opt_slice: &'a Option<Box<Pat<'tcx>>>,
23 suffix: &'a [Pat<'tcx>],
24) -> Vec<(PlaceBuilder<'tcx>, &'a Pat<'tcx>)> {
25let prefix_len = u64::try_from(prefix.len()).unwrap();
26let suffix_len = u64::try_from(suffix.len()).unwrap();
2728let mut output_pairs =
29Vec::with_capacity(prefix.len() + usize::from(opt_slice.is_some()) + suffix.len());
3031// For slice patterns with a `..` followed by 0 or more suffix subpatterns,
32 // the actual slice index of those subpatterns isn't statically known, so
33 // we have to index them relative to the end of the slice.
34 //
35 // For array patterns, all subpatterns are indexed relative to the start.
36let (min_length, is_array) = match array_len {
37Some(len) => (len, true),
38None => (prefix_len + suffix_len, false),
39 };
4041for (offset, prefix_subpat) in (0u64..).zip(prefix) {
42let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
43let subplace = place.clone_project(elem);
44 output_pairs.push((subplace, prefix_subpat));
45 }
4647if let Some(slice_subpat) = opt_slice {
48let elem = PlaceElem::Subslice {
49 from: prefix_len,
50 to: if is_array { min_length - suffix_len } else { suffix_len },
51 from_end: !is_array,
52 };
53let subplace = place.clone_project(elem);
54output_pairs.push((subplace, slice_subpat));
55 }
5657for (offset_from_end, suffix_subpat) in (1u64..).zip(suffix.iter().rev()) {
58let elem = ProjectionElem::ConstantIndex {
59 offset: if is_array { min_length - offset_from_end } else { offset_from_end },
60 min_length,
61 from_end: !is_array,
62 };
63let subplace = place.clone_project(elem);
64 output_pairs.push((subplace, suffix_subpat));
65 }
6667output_pairs68}
6970impl<'tcx> FlatPat<'tcx> {
71/// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest
72 /// for the given pattern.
73pub(crate) fn new(
74 place: PlaceBuilder<'tcx>,
75 pattern: &Pat<'tcx>,
76 cx: &mut Builder<'_, 'tcx>,
77 ) -> Self {
78// Recursively lower the THIR pattern into an intermediate form,
79 // then flatten into a `FlatPat`.
80let inter_pat = InterPat::lower_thir_pat(cx, place, pattern);
81FlatPat::from_inter_pat(inter_pat)
82 }
8384/// Squashes an [`InterPat`] into a [`FlatPat`].
85 ///
86 /// This is a separate function because it also needs to be called recursively
87 /// when squashing any or-patterns.
88fn from_inter_pat(inter_pat: InterPat<'tcx>) -> Self {
89let mut match_pairs = ::alloc::vec::Vec::new()vec![];
90let mut extra_data = PatternExtraData {
91 span: inter_pat.pattern_span,
92 bindings: ::alloc::vec::Vec::new()vec![],
93 ascriptions: ::alloc::vec::Vec::new()vec![],
94 is_never: inter_pat.is_never,
95 };
96squash_inter_pat(inter_pat, &mut match_pairs, &mut extra_data);
9798FlatPat { match_pairs, extra_data }
99 }
100}
101102/// Recursively squashes an [`InterPat`] into a forest of refutable [`MatchPairTree`]
103/// nodes, while accumulating ascriptions and bindings.
104fn squash_inter_pat<'tcx>(
105 inter_pat: InterPat<'tcx>,
106 match_pairs: &mut Vec<MatchPairTree<'tcx>>, // Newly-created nodes are added to this vector
107extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
108) {
109// Destructure exhaustively to make sure we don't miss any fields.
110let InterPat {
111 place,
112 testable_case,
113 subpats,
114 or_subpats,
115 ascriptions,
116 binding,
117 pattern_span,
118 is_never: _, // Not needed by `MatchPairTree` forests.
119} = inter_pat;
120121// Type ascriptions can appear regardless of whether the node is an or-pattern.
122extra_data.ascriptions.extend(ascriptions);
123124// Or and non-or patterns have very different handling.
125if let Some(or_subpats) = or_subpats {
126// We're dealing with an or-pattern node.
127if !testable_case.is_none() {
::core::panicking::panic("assertion failed: testable_case.is_none()")
};assert!(testable_case.is_none());
128if !subpats.is_empty() {
::core::panicking::panic("assertion failed: subpats.is_empty()")
};assert!(subpats.is_empty());
129if !binding.is_none() {
::core::panicking::panic("assertion failed: binding.is_none()")
};assert!(binding.is_none());
130131let or_subpats = or_subpats132 .into_iter()
133 .map(|subpat| FlatPat::from_inter_pat(subpat))
134 .collect::<Box<[_]>>();
135136if !or_subpats[0].extra_data.bindings.is_empty() {
137// Hold a place for any bindings established in (possibly-nested) or-patterns.
138 // By only holding a place when bindings are present, we skip over any
139 // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
140 // other words, we can assume this expands into subcandidates.
141 // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
142extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
143 }
144145match_pairs.push(MatchPairTree {
146// Or-patterns never need a place during MIR building.
147place: None,
148 testable_case: TestableCase::Or { pats: or_subpats },
149 subpairs: ::alloc::vec::Vec::new()vec![],
150pattern_span,
151 });
152 } else {
153// We're dealing with a node that isn't an or-pattern.
154155 // Recursively squash any subpatterns into refutable `MatchPairTree` forests.
156 // This must happen _before_ pushing the binding, as described by the binding step.
157let mut subpairs = ::alloc::vec::Vec::new()vec![];
158for subpat in subpats {
159 squash_inter_pat(subpat, &mut subpairs, extra_data);
160 }
161162if let Some(testable_case) = testable_case {
163// This pattern is refutable, so push a new match-pair node.
164 //
165 // If this match is inside a closure, it's essential that the place
166 // we're testing was actually captured! Be sure to keep `ExprUseVisitor`
167 // in sync with the refutability checks in this module.
168if !place.is_some() {
::core::panicking::panic("assertion failed: place.is_some()")
};assert!(place.is_some());
169if !!#[allow(non_exhaustive_omitted_patterns)] match testable_case {
TestableCase::Or { .. } => true,
_ => false,
} {
::core::panicking::panic("assertion failed: !matches!(testable_case, TestableCase::Or { .. })")
};assert!(!matches!(testable_case, TestableCase::Or { .. }));
170match_pairs.push(MatchPairTree { place, testable_case, subpairs, pattern_span });
171 } else {
172// This pattern is irrefutable, so it doesn't need its own match-pair node.
173 // Just push its refutable subpatterns instead, if any.
174match_pairs.extend(subpairs);
175 }
176177// If present, the binding must be pushed _after_ traversing subpatterns.
178 // This is so that when lowering something like `x @ NonCopy { copy_field }`,
179 // the binding to `copy_field` will occur before the binding for `x`.
180 // See <https://github.com/rust-lang/rust/issues/69971> for more background.
181if let Some(binding) = binding {
182extra_data.bindings.push(super::SubpatternBindings::One(binding));
183 }
184 }
185}
186187/// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been
188/// squashed into a forest of refutable [`MatchPairTree`] nodes.
189///
190/// FIXME(Zalathar): This could potentially be split into different enum variants
191/// for or-patterns and non-or patterns, but for now the flat structure makes
192/// construction a bit easier, at the cost of more complicated invariants.
193struct InterPat<'tcx> {
194/// Place that this pattern node will test.
195 ///
196 /// If `None`, we're in a closure that didn't capture the relevant place,
197 /// because it won't actually be tested.
198place: Option<Place<'tcx>>,
199/// Testable condition to compare the place to (e.g. "is 3" or "is Some").
200 ///
201 /// If `None`, this pattern node is irrefutable or an or-pattern,
202 /// though it might have refutable descendants.
203testable_case: Option<TestableCase<'tcx>>,
204205/// Immediate subpatterns of a node that is *not* an or-pattern.
206subpats: Vec<InterPat<'tcx>>,
207/// Immediate subpatterns of an or-pattern node.
208 ///
209 /// Invariant: If this is Some, then fields `subpats`, `testable_case`,
210 /// and `binding` must all be empty.
211or_subpats: Option<Box<[InterPat<'tcx>]>>,
212213 ascriptions: Vec<super::Ascription<'tcx>>,
214/// Binding to establish for a [`PatKind::Binding`] node.
215binding: Option<super::Binding<'tcx>>,
216217/// Span field of the THIR pattern this node was created from.
218pattern_span: Span,
219/// True if this pattern can never match, because all of its alternatives
220 /// contain a `!` pattern.
221is_never: bool,
222}
223224impl<'tcx> InterPat<'tcx> {
225fn lower_thir_pat(
226 cx: &mut Builder<'_, 'tcx>,
227mut place_builder: PlaceBuilder<'tcx>,
228 pattern: &Pat<'tcx>,
229 ) -> Self {
230// Force the place type to the pattern's type.
231 // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
232if let Some(resolved) = place_builder.resolve_upvar(cx) {
233place_builder = resolved;
234 }
235236if !cx.tcx.next_trait_solver_globally() {
237// Only add the OpaqueCast projection if the given place is an opaque type and the
238 // expected type from the pattern is not.
239let may_need_cast = match place_builder.base() {
240 PlaceBase::Local(local) => {
241let ty =
242Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
243 .ty;
244ty != pattern.ty && ty.has_opaque_types()
245 }
246_ => true,
247 };
248if may_need_cast {
249place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
250 }
251 }
252253// Variables that will become `InterPat` fields:
254let place = place_builder.try_to_place(cx);
255let mut subpats = ::alloc::vec::Vec::new()vec![];
256let mut or_subpats = None;
257let mut ascriptions = ::alloc::vec::Vec::new()vec![];
258let mut binding = None;
259260// Apply any type ascriptions to the value at `match_pair.place`.
261if let Some(place) = place262 && let Some(extra) = &pattern.extra
263 {
264for &Ascription { ref annotation, variance } in &extra.ascriptions {
265 ascriptions.push(super::Ascription {
266 source: place,
267 annotation: annotation.clone(),
268 variance,
269 });
270 }
271 }
272273let testable_case = match pattern.kind {
274 PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
275276 PatKind::Or { ref pats } => {
277or_subpats = Some(
278pats.iter()
279 .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat))
280 .collect::<Box<[_]>>(),
281 );
282None283 }
284285 PatKind::Range(ref range) => {
286match (&pattern.ty, &range.ty) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(pattern.ty, range.ty);
287if range.is_full_range(cx.tcx) == Some(true) {
288None289 } else {
290Some(TestableCase::Range(Arc::clone(range)))
291 }
292 }
293294 PatKind::Constant { value } => {
295match (&pattern.ty, &value.ty) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(pattern.ty, value.ty);
296297// Classify the constant-pattern into further kinds, to
298 // reduce the number of ad-hoc type tests needed later on.
299let pat_ty = pattern.ty;
300let const_kind = if pat_ty.is_bool() {
301 PatConstKind::Bool302 } else if pat_ty.is_integral() || pat_ty.is_char() {
303 PatConstKind::IntOrChar304 } else if pat_ty.is_floating_point() {
305 PatConstKind::Float306 } else if pat_ty.is_str() {
307 PatConstKind::String308 } else {
309// FIXME(Zalathar): This still covers several different
310 // categories (e.g. raw pointer, pattern-type)
311 // which could be split out into their own kinds.
312PatConstKind::Other313 };
314Some(TestableCase::Constant { value, kind: const_kind })
315 }
316317 PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
318// First, recurse into the subpattern, if any.
319if let Some(subpattern) = subpattern.as_ref() {
320// this is the `x @ P` case; have to keep matching against `P` now
321subpats.push(InterPat::lower_thir_pat(cx, place_builder, subpattern));
322 }
323324// Then push this binding, after any bindings in the subpattern.
325if let Some(place) = place {
326binding = Some(super::Binding {
327 span: pattern.span,
328 source: place,
329 var_id: var,
330 binding_mode: mode,
331is_shorthand,
332 });
333 }
334None335 }
336337 PatKind::Array { ref prefix, ref slice, ref suffix } => {
338// Determine the statically-known length of the array type being matched.
339 // This should always succeed for legal programs, but could fail for
340 // erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
341 // so take care not to ICE if this fails.
342let array_len = match pattern.ty.kind() {
343 ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
344_ => None,
345 };
346if let Some(array_len) = array_len {
347for (subplace, subpat) in
348prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix)
349 {
350 subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
351 }
352 } else {
353// If the array length couldn't be determined, ignore the
354 // subpatterns and delayed-assert that compilation will fail.
355cx.tcx.dcx().span_delayed_bug(
356pattern.span,
357::alloc::__export::must_use({
::alloc::fmt::format(format_args!("array length in pattern couldn\'t be determined for ty={0:?}",
pattern.ty))
})format!(
358"array length in pattern couldn't be determined for ty={:?}",
359 pattern.ty
360 ),
361 );
362 }
363364None365 }
366 PatKind::Slice { ref prefix, ref slice, ref suffix } => {
367for (subplace, subpat) in
368prefix_slice_suffix(&place_builder, None, prefix, slice, suffix)
369 {
370 subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
371 }
372373if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
374// A slice pattern shaped like `[..]` is irrefutable.
375 // It can match a slice of any length, so no length test is needed.
376None377 } else {
378// Any other shape of slice pattern requires a length test.
379 // Slice patterns with a `..` subpattern require a minimum
380 // length; those without `..` require an exact length.
381Some(TestableCase::Slice {
382 len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
383 op: if slice.is_some() {
384 SliceLenOp::GreaterOrEqual385 } else {
386 SliceLenOp::Equal387 },
388 })
389 }
390 }
391392 PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
393let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
394for &FieldPat { field, pattern: ref subpat } in subpatterns {
395let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty));
396 subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
397 }
398399// We treat non-exhaustive enums the same independent of the crate they are
400 // defined in, to avoid differences in the operational semantics between crates.
401let refutable =
402adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
403if refutable {
404Some(TestableCase::Variant { adt_def, variant_index })
405 } else {
406None407 }
408 }
409410 PatKind::Leaf { ref subpatterns } => {
411for &FieldPat { field, pattern: ref subpat } in subpatterns {
412let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty));
413 subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
414 }
415None416 }
417418 PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => {
419let pinned_ref_ty = match pattern.ty.pinned_ty() {
420Some(p_ty) if p_ty.is_ref() => p_ty,
421_ => ::rustc_middle::util::bug::span_bug_fmt(pattern.span,
format_args!("bad type for pinned deref: {0:?}", pattern.ty))span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty),
422 };
423subpats.push(InterPat::lower_thir_pat(
424cx,
425// Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`.
426place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(),
427subpattern,
428 ));
429430None431 }
432433 PatKind::Deref { pin: Pinnedness::Not, ref subpattern }
434 | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
435subpats.push(InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern));
436None437 }
438439 PatKind::DerefPattern {
440ref subpattern,
441 borrow: DerefPatBorrowMode::Borrow(mutability),
442 } => {
443// Create a new temporary for each deref pattern.
444 // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
445let temp = cx.temp(
446Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
447pattern.span,
448 );
449subpats.push(InterPat::lower_thir_pat(
450cx,
451PlaceBuilder::from(temp).deref(),
452subpattern,
453 ));
454Some(TestableCase::Deref { temp, mutability })
455 }
456457 PatKind::Guard { .. } => {
458// FIXME(guard_patterns)
459None460 }
461462 PatKind::Never => Some(TestableCase::Never),
463 };
464465// A pattern node is guaranteed to never match if one of these is true:
466 // - The node itself is a never pattern (`!`).
467 // - It is not an or-pattern, and one of its subpatterns will never match.
468 // - It is an or-pattern, and _all_ of its or-subpatterns will never match.
469let is_never = #[allow(non_exhaustive_omitted_patterns)] match pattern.kind {
PatKind::Never => true,
_ => false,
}matches!(pattern.kind, PatKind::Never)470 || subpats.iter().any(|subpat| subpat.is_never)
471 || or_subpats472 .as_ref()
473 .is_some_and(|or_subpats| or_subpats.iter().all(|subpat| subpat.is_never));
474475InterPat {
476place,
477testable_case,
478subpats,
479or_subpats,
480ascriptions,
481binding,
482 pattern_span: pattern.span,
483is_never,
484 }
485 }
486}