]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/build/matches/simplify.rs
Rollup merge of #105555 - krasimirgg:llvm-int-opt-2, r=cuviper
[rust.git] / compiler / rustc_mir_build / src / build / matches / simplify.rs
1 //! Simplifying Candidates
2 //!
3 //! *Simplifying* a match pair `place @ pattern` means breaking it down
4 //! into bindings or other, simpler match pairs. For example:
5 //!
6 //! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
7 //! - `place @ x` can be simplified to `[]` by binding `x` to `place`
8 //!
9 //! The `simplify_candidate` routine just repeatedly applies these
10 //! sort of simplifications until there is nothing left to
11 //! simplify. Match pairs cannot be simplified if they require some
12 //! sort of test: for example, testing which variant an enum is, or
13 //! testing a value against a constant.
14
15 use crate::build::expr::as_place::PlaceBuilder;
16 use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
17 use crate::build::Builder;
18 use rustc_hir::RangeEnd;
19 use rustc_middle::thir::{self, *};
20 use rustc_middle::ty;
21 use rustc_middle::ty::layout::IntegerExt;
22 use rustc_target::abi::{Integer, Size};
23
24 use std::mem;
25
26 impl<'a, 'tcx> Builder<'a, 'tcx> {
27     /// Simplify a candidate so that all match pairs require a test.
28     ///
29     /// This method will also split a candidate, in which the only
30     /// match-pair is an or-pattern, into multiple candidates.
31     /// This is so that
32     ///
33     /// match x {
34     ///     0 | 1 => { ... },
35     ///     2 | 3 => { ... },
36     /// }
37     ///
38     /// only generates a single switch. If this happens this method returns
39     /// `true`.
40     #[instrument(skip(self, candidate), level = "debug")]
41     pub(super) fn simplify_candidate<'pat>(
42         &mut self,
43         candidate: &mut Candidate<'pat, 'tcx>,
44     ) -> bool {
45         // repeatedly simplify match pairs until fixed point is reached
46         debug!("{candidate:#?}");
47
48         // existing_bindings and new_bindings exists to keep the semantics in order.
49         // Reversing the binding order for bindings after `@` changes the binding order in places
50         // it shouldn't be changed, for example `let (Some(a), Some(b)) = (x, y)`
51         //
52         // To avoid this, the binding occurs in the following manner:
53         // * the bindings for one iteration of the following loop occurs in order (i.e. left to
54         // right)
55         // * the bindings from the previous iteration of the loop is prepended to the bindings from
56         // the current iteration (in the implementation this is done by mem::swap and extend)
57         // * after all iterations, these new bindings are then appended to the bindings that were
58         // preexisting (i.e. `candidate.binding` when the function was called).
59         //
60         // example:
61         // candidate.bindings = [1, 2, 3]
62         // binding in iter 1: [4, 5]
63         // binding in iter 2: [6, 7]
64         //
65         // final binding: [1, 2, 3, 6, 7, 4, 5]
66         let mut existing_bindings = mem::take(&mut candidate.bindings);
67         let mut new_bindings = Vec::new();
68         loop {
69             let match_pairs = mem::take(&mut candidate.match_pairs);
70
71             if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] =
72                 &*match_pairs
73             {
74                 existing_bindings.extend_from_slice(&new_bindings);
75                 mem::swap(&mut candidate.bindings, &mut existing_bindings);
76                 candidate.subcandidates = self.create_or_subcandidates(candidate, &place, pats);
77                 return true;
78             }
79
80             let mut changed = false;
81             for match_pair in match_pairs {
82                 match self.simplify_match_pair(match_pair, candidate) {
83                     Ok(()) => {
84                         changed = true;
85                     }
86                     Err(match_pair) => {
87                         candidate.match_pairs.push(match_pair);
88                     }
89                 }
90             }
91             // Avoid issue #69971: the binding order should be right to left if there are more
92             // bindings after `@` to please the borrow checker
93             // Ex
94             // struct NonCopyStruct {
95             //     copy_field: u32,
96             // }
97             //
98             // fn foo1(x: NonCopyStruct) {
99             //     let y @ NonCopyStruct { copy_field: z } = x;
100             //     // the above should turn into
101             //     let z = x.copy_field;
102             //     let y = x;
103             // }
104             candidate.bindings.extend_from_slice(&new_bindings);
105             mem::swap(&mut candidate.bindings, &mut new_bindings);
106             candidate.bindings.clear();
107
108             if !changed {
109                 existing_bindings.extend_from_slice(&new_bindings);
110                 mem::swap(&mut candidate.bindings, &mut existing_bindings);
111                 // Move or-patterns to the end, because they can result in us
112                 // creating additional candidates, so we want to test them as
113                 // late as possible.
114                 candidate
115                     .match_pairs
116                     .sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
117                 debug!(simplified = ?candidate, "simplify_candidate");
118                 return false; // if we were not able to simplify any, done.
119             }
120         }
121     }
122
123     /// Given `candidate` that has a single or-pattern for its match-pairs,
124     /// creates a fresh candidate for each of its input subpatterns passed via
125     /// `pats`.
126     fn create_or_subcandidates<'pat>(
127         &mut self,
128         candidate: &Candidate<'pat, 'tcx>,
129         place: &PlaceBuilder<'tcx>,
130         pats: &'pat [Box<Pat<'tcx>>],
131     ) -> Vec<Candidate<'pat, 'tcx>> {
132         pats.iter()
133             .map(|box pat| {
134                 let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self);
135                 self.simplify_candidate(&mut candidate);
136                 candidate
137             })
138             .collect()
139     }
140
141     /// Tries to simplify `match_pair`, returning `Ok(())` if
142     /// successful. If successful, new match pairs and bindings will
143     /// have been pushed into the candidate. If no simplification is
144     /// possible, `Err` is returned and no changes are made to
145     /// candidate.
146     fn simplify_match_pair<'pat>(
147         &mut self,
148         match_pair: MatchPair<'pat, 'tcx>,
149         candidate: &mut Candidate<'pat, 'tcx>,
150     ) -> Result<(), MatchPair<'pat, 'tcx>> {
151         let tcx = self.tcx;
152         match match_pair.pattern.kind {
153             PatKind::AscribeUserType {
154                 ref subpattern,
155                 ascription: thir::Ascription { ref annotation, variance },
156             } => {
157                 // Apply the type ascription to the value at `match_pair.place`, which is the
158                 if let Some(source) = match_pair.place.try_to_place(self) {
159                     candidate.ascriptions.push(Ascription {
160                         annotation: annotation.clone(),
161                         source,
162                         variance,
163                     });
164                 }
165
166                 candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
167
168                 Ok(())
169             }
170
171             PatKind::Wild => {
172                 // nothing left to do
173                 Ok(())
174             }
175
176             PatKind::Binding {
177                 name: _,
178                 mutability: _,
179                 mode,
180                 var,
181                 ty: _,
182                 ref subpattern,
183                 is_primary: _,
184             } => {
185                 if let Some(source) = match_pair.place.try_to_place(self) {
186                     candidate.bindings.push(Binding {
187                         span: match_pair.pattern.span,
188                         source,
189                         var_id: var,
190                         binding_mode: mode,
191                     });
192                 }
193
194                 if let Some(subpattern) = subpattern.as_ref() {
195                     // this is the `x @ P` case; have to keep matching against `P` now
196                     candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
197                 }
198
199                 Ok(())
200             }
201
202             PatKind::Constant { .. } => {
203                 // FIXME normalize patterns when possible
204                 Err(match_pair)
205             }
206
207             PatKind::Range(box PatRange { lo, hi, end }) => {
208                 let (range, bias) = match *lo.ty().kind() {
209                     ty::Char => {
210                         (Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
211                     }
212                     ty::Int(ity) => {
213                         let size = Integer::from_int_ty(&tcx, ity).size();
214                         let max = size.truncate(u128::MAX);
215                         let bias = 1u128 << (size.bits() - 1);
216                         (Some((0, max, size)), bias)
217                     }
218                     ty::Uint(uty) => {
219                         let size = Integer::from_uint_ty(&tcx, uty).size();
220                         let max = size.truncate(u128::MAX);
221                         (Some((0, max, size)), 0)
222                     }
223                     _ => (None, 0),
224                 };
225                 if let Some((min, max, sz)) = range {
226                     // We want to compare ranges numerically, but the order of the bitwise
227                     // representation of signed integers does not match their numeric order. Thus,
228                     // to correct the ordering, we need to shift the range of signed integers to
229                     // correct the comparison. This is achieved by XORing with a bias (see
230                     // pattern/_match.rs for another pertinent example of this pattern).
231                     //
232                     // Also, for performance, it's important to only do the second `try_to_bits` if
233                     // necessary.
234                     let lo = lo.try_to_bits(sz).unwrap() ^ bias;
235                     if lo <= min {
236                         let hi = hi.try_to_bits(sz).unwrap() ^ bias;
237                         if hi > max || hi == max && end == RangeEnd::Included {
238                             // Irrefutable pattern match.
239                             return Ok(());
240                         }
241                     }
242                 }
243                 Err(match_pair)
244             }
245
246             PatKind::Slice { ref prefix, ref slice, ref suffix } => {
247                 if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
248                     // irrefutable
249                     self.prefix_slice_suffix(
250                         &mut candidate.match_pairs,
251                         &match_pair.place,
252                         prefix,
253                         slice,
254                         suffix,
255                     );
256                     Ok(())
257                 } else {
258                     Err(match_pair)
259                 }
260             }
261
262             PatKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
263                 let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
264                     i == variant_index || {
265                         self.tcx.features().exhaustive_patterns
266                             && !v
267                                 .inhabited_predicate(self.tcx, adt_def)
268                                 .subst(self.tcx, substs)
269                                 .apply_ignore_module(self.tcx, self.param_env)
270                     }
271                 }) && (adt_def.did().is_local()
272                     || !adt_def.is_variant_list_non_exhaustive());
273                 if irrefutable {
274                     let place_builder = match_pair.place.downcast(adt_def, variant_index);
275                     candidate
276                         .match_pairs
277                         .extend(self.field_match_pairs(place_builder, subpatterns));
278                     Ok(())
279                 } else {
280                     Err(match_pair)
281                 }
282             }
283
284             PatKind::Array { ref prefix, ref slice, ref suffix } => {
285                 self.prefix_slice_suffix(
286                     &mut candidate.match_pairs,
287                     &match_pair.place,
288                     prefix,
289                     slice,
290                     suffix,
291                 );
292                 Ok(())
293             }
294
295             PatKind::Leaf { ref subpatterns } => {
296                 // tuple struct, match subpats (if any)
297                 candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
298                 Ok(())
299             }
300
301             PatKind::Deref { ref subpattern } => {
302                 let place_builder = match_pair.place.deref();
303                 candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self));
304                 Ok(())
305             }
306
307             PatKind::Or { .. } => Err(match_pair),
308         }
309     }
310 }