]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/matches/test.rs
74402ccba348f2ab578d38806f9ad27e9d51af47
[rust.git] / src / librustc_mir / build / matches / test.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Testing candidates
12 //
13 // After candidates have been simplified, the only match pairs that
14 // remain are those that require some sort of test. The functions here
15 // identify what tests are needed, perform the tests, and then filter
16 // the candidates based on the result.
17
18 use build::Builder;
19 use build::matches::{Candidate, MatchPair, Test, TestKind};
20 use hair::*;
21 use rustc_data_structures::fnv::FnvHashMap;
22 use rustc::middle::const_eval::ConstVal;
23 use rustc::middle::ty::{self, Ty};
24 use rustc::mir::repr::*;
25 use syntax::codemap::Span;
26
27 impl<'a,'tcx> Builder<'a,'tcx> {
28     /// Identifies what test is needed to decide if `match_pair` is applicable.
29     ///
30     /// It is a bug to call this with a simplifyable pattern.
31     pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
32         match *match_pair.pattern.kind {
33             PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => {
34                 Test {
35                     span: match_pair.pattern.span,
36                     kind: TestKind::Switch { adt_def: adt_def.clone() },
37                 }
38             }
39
40             PatternKind::Constant { .. }
41             if is_switch_ty(match_pair.pattern.ty) => {
42                 // for integers, we use a SwitchInt match, which allows
43                 // us to handle more cases
44                 Test {
45                     span: match_pair.pattern.span,
46                     kind: TestKind::SwitchInt {
47                         switch_ty: match_pair.pattern.ty,
48
49                         // these maps are empty to start; cases are
50                         // added below in add_cases_to_switch
51                         options: vec![],
52                         indices: FnvHashMap(),
53                     }
54                 }
55             }
56
57             PatternKind::Constant { ref value } => {
58                 Test {
59                     span: match_pair.pattern.span,
60                     kind: TestKind::Eq {
61                         value: value.clone(),
62                         ty: match_pair.pattern.ty.clone()
63                     }
64                 }
65             }
66
67             PatternKind::Range { ref lo, ref hi } => {
68                 Test {
69                     span: match_pair.pattern.span,
70                     kind: TestKind::Range {
71                         lo: lo.clone(),
72                         hi: hi.clone(),
73                         ty: match_pair.pattern.ty.clone(),
74                     },
75                 }
76             }
77
78             PatternKind::Slice { ref prefix, ref slice, ref suffix }
79                     if !match_pair.slice_len_checked => {
80                 let len = prefix.len() + suffix.len();
81                 let op = if slice.is_some() {
82                     BinOp::Ge
83                 } else {
84                     BinOp::Eq
85                 };
86                 Test {
87                     span: match_pair.pattern.span,
88                     kind: TestKind::Len { len: len as u64, op: op },
89                 }
90             }
91
92             PatternKind::Array { .. } |
93             PatternKind::Slice { .. } |
94             PatternKind::Wild |
95             PatternKind::Binding { .. } |
96             PatternKind::Leaf { .. } |
97             PatternKind::Deref { .. } => {
98                 self.error_simplifyable(match_pair)
99             }
100         }
101     }
102
103     pub fn add_cases_to_switch<'pat>(&mut self,
104                                      test_lvalue: &Lvalue<'tcx>,
105                                      candidate: &Candidate<'pat, 'tcx>,
106                                      switch_ty: Ty<'tcx>,
107                                      options: &mut Vec<ConstVal>,
108                                      indices: &mut FnvHashMap<ConstVal, usize>)
109                                      -> bool
110     {
111         let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
112             Some(match_pair) => match_pair,
113             _ => { return false; }
114         };
115
116         match *match_pair.pattern.kind {
117             PatternKind::Constant { ref value } => {
118                 // if the lvalues match, the type should match
119                 assert_eq!(match_pair.pattern.ty, switch_ty);
120
121                 indices.entry(value.clone())
122                        .or_insert_with(|| {
123                            options.push(value.clone());
124                            options.len() - 1
125                        });
126                 true
127             }
128
129             PatternKind::Range { .. } |
130             PatternKind::Variant { .. } |
131             PatternKind::Slice { .. } |
132             PatternKind::Array { .. } |
133             PatternKind::Wild |
134             PatternKind::Binding { .. } |
135             PatternKind::Leaf { .. } |
136             PatternKind::Deref { .. } => {
137                 // don't know how to add these patterns to a switch
138                 false
139             }
140         }
141     }
142
143     /// Generates the code to perform a test.
144     pub fn perform_test(&mut self,
145                         block: BasicBlock,
146                         lvalue: &Lvalue<'tcx>,
147                         test: &Test<'tcx>)
148                         -> Vec<BasicBlock> {
149         let scope_id = self.innermost_scope_id();
150         match test.kind {
151             TestKind::Switch { adt_def } => {
152                 let num_enum_variants = self.hir.num_variants(adt_def);
153                 let target_blocks: Vec<_> =
154                     (0..num_enum_variants).map(|_| self.cfg.start_new_block())
155                                           .collect();
156                 self.cfg.terminate(block, TerminatorKind::Switch {
157                     discr: lvalue.clone(),
158                     adt_def: adt_def,
159                     targets: target_blocks.clone()
160                 });
161                 target_blocks
162             }
163
164             TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
165                 let otherwise = self.cfg.start_new_block();
166                 let targets: Vec<_> =
167                     options.iter()
168                            .map(|_| self.cfg.start_new_block())
169                            .chain(Some(otherwise))
170                            .collect();
171                 self.cfg.terminate(block, TerminatorKind::SwitchInt {
172                     discr: lvalue.clone(),
173                     switch_ty: switch_ty,
174                     values: options.clone(),
175                     targets: targets.clone(),
176                 });
177                 targets
178             }
179
180             TestKind::Eq { ref value, mut ty } => {
181                 let mut val = Operand::Consume(lvalue.clone());
182
183                 // If we're using b"..." as a pattern, we need to insert an
184                 // unsizing coercion, as the byte string has the type &[u8; N].
185                 let expect = if let ConstVal::ByteStr(ref bytes) = *value {
186                     let tcx = self.hir.tcx();
187
188                     // Unsize the lvalue to &[u8], too, if necessary.
189                     if let ty::TyRef(region, mt) = ty.sty {
190                         if let ty::TyArray(_, _) = mt.ty.sty {
191                             ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
192                             let val_slice = self.temp(ty);
193                             self.cfg.push_assign(block, scope_id, test.span, &val_slice,
194                                                  Rvalue::Cast(CastKind::Unsize, val, ty));
195                             val = Operand::Consume(val_slice);
196                         }
197                     }
198
199                     assert!(ty.is_slice());
200
201                     let array_ty = tcx.mk_array(tcx.types.u8, bytes.len());
202                     let array_ref = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), array_ty);
203                     let array = self.literal_operand(test.span, array_ref, Literal::Value {
204                         value: value.clone()
205                     });
206
207                     let slice = self.temp(ty);
208                     self.cfg.push_assign(block, scope_id, test.span, &slice,
209                                          Rvalue::Cast(CastKind::Unsize, array, ty));
210                     Operand::Consume(slice)
211                 } else {
212                     self.literal_operand(test.span, ty, Literal::Value {
213                         value: value.clone()
214                     })
215                 };
216
217                 // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
218                 let fail = self.cfg.start_new_block();
219                 if let ty::TyRef(_, mt) = ty.sty {
220                     assert!(ty.is_slice());
221                     let eq_def_id = self.hir.tcx().lang_items.eq_trait().unwrap();
222                     let ty = mt.ty;
223                     let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, vec![ty]);
224
225                     let bool_ty = self.hir.bool_ty();
226                     let eq_result = self.temp(bool_ty);
227                     let eq_block = self.cfg.start_new_block();
228                     let cleanup = self.diverge_cleanup();
229                     self.cfg.terminate(block, Terminator::Call {
230                         func: Operand::Constant(Constant {
231                             span: test.span,
232                             ty: mty,
233                             literal: method
234                         }),
235                         args: vec![val, expect],
236                         destination: Some((eq_result.clone(), eq_block)),
237                         cleanup: cleanup,
238                     });
239
240                     // check the result
241                     let block = self.cfg.start_new_block();
242                     self.cfg.terminate(eq_block, Terminator::If {
243                         cond: Operand::Consume(eq_result),
244                         targets: (block, fail),
245                     });
246
247                     vec![block, fail]
248                 } else {
249                     let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
250                     vec![block, fail]
251                 }
252             }
253
254             TestKind::Range { ref lo, ref hi, ty } => {
255                 // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
256                 let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
257                 let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
258                 let val = Operand::Consume(lvalue.clone());
259
260                 let fail = self.cfg.start_new_block();
261                 let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone());
262                 let block = self.compare(block, fail, test.span, BinOp::Le, val, hi);
263
264                 vec![block, fail]
265             }
266
267             TestKind::Len { len, op } => {
268                 let (usize_ty, bool_ty) = (self.hir.usize_ty(), self.hir.bool_ty());
269                 let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
270
271                 // actual = len(lvalue)
272                 self.cfg.push_assign(block, scope_id, test.span, &actual, Rvalue::Len(lvalue.clone()));
273
274                 // expected = <N>
275                 let expected = self.push_usize(block, scope_id, test.span, len);
276
277                 // result = actual == expected OR result = actual < expected
278                 self.cfg.push_assign(block,
279                                      scope_id,
280                                      test.span,
281                                      &result,
282                                      Rvalue::BinaryOp(op,
283                                                       Operand::Consume(actual),
284                                                       Operand::Consume(expected)));
285
286                 // branch based on result
287                 let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
288                                                  self.cfg.start_new_block()];
289                 self.cfg.terminate(block, TerminatorKind::If {
290                     cond: Operand::Consume(result),
291                     targets: (target_blocks[0], target_blocks[1])
292                 });
293
294                 target_blocks
295             }
296         }
297     }
298
299     fn compare(&mut self,
300                block: BasicBlock,
301                fail_block: BasicBlock,
302                span: Span,
303                op: BinOp,
304                left: Operand<'tcx>,
305                right: Operand<'tcx>) -> BasicBlock {
306         let bool_ty = self.hir.bool_ty();
307         let result = self.temp(bool_ty);
308
309         // result = op(left, right)
310         let scope_id = self.innermost_scope_id();
311         self.cfg.push_assign(block, scope_id, span, &result,
312                              Rvalue::BinaryOp(op, left, right));
313
314         // branch based on result
315         let target_block = self.cfg.start_new_block();
316         self.cfg.terminate(block, TerminatorKind::If {
317             cond: Operand::Consume(result),
318             targets: (target_block, fail_block)
319         });
320
321         target_block
322     }
323
324     /// Given that we are performing `test` against `test_lvalue`,
325     /// this job sorts out what the status of `candidate` will be
326     /// after the test. The `resulting_candidates` vector stores, for
327     /// each possible outcome of `test`, a vector of the candidates
328     /// that will result. This fn should add a (possibly modified)
329     /// clone of candidate into `resulting_candidates` wherever
330     /// appropriate.
331     ///
332     /// So, for example, if this candidate is `x @ Some(P0)` and the
333     /// test is a variant test, then we would add `(x as Option).0 @
334     /// P0` to the `resulting_candidates` entry corresponding to the
335     /// variant `Some`.
336     ///
337     /// However, in some cases, the test may just not be relevant to
338     /// candidate. For example, suppose we are testing whether `foo.x == 22`,
339     /// but in one match arm we have `Foo { x: _, ... }`... in that case,
340     /// the test for what value `x` has has no particular relevance
341     /// to this candidate. In such cases, this function just returns false
342     /// without doing anything. This is used by the overall `match_candidates`
343     /// algorithm to structure the match as a whole. See `match_candidates` for
344     /// more details.
345     ///
346     /// FIXME(#29623). In some cases, we have some tricky choices to
347     /// make.  for example, if we are testing that `x == 22`, but the
348     /// candidate is `x @ 13..55`, what should we do? In the event
349     /// that the test is true, we know that the candidate applies, but
350     /// in the event of false, we don't know that it *doesn't*
351     /// apply. For now, we return false, indicate that the test does
352     /// not apply to this candidate, but it might be we can get
353     /// tighter match code if we do something a bit different.
354     pub fn sort_candidate<'pat>(&mut self,
355                                 test_lvalue: &Lvalue<'tcx>,
356                                 test: &Test<'tcx>,
357                                 candidate: &Candidate<'pat, 'tcx>,
358                                 resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>])
359                                 -> bool {
360         // Find the match_pair for this lvalue (if any). At present,
361         // afaik, there can be at most one. (In the future, if we
362         // adopted a more general `@` operator, there might be more
363         // than one, but it'd be very unusual to have two sides that
364         // both require tests; you'd expect one side to be simplified
365         // away.)
366         let tested_match_pair = candidate.match_pairs.iter()
367                                                      .enumerate()
368                                                      .filter(|&(_, mp)| mp.lvalue == *test_lvalue)
369                                                      .next();
370         let (match_pair_index, match_pair) = match tested_match_pair {
371             Some(pair) => pair,
372             None => {
373                 // We are not testing this lvalue. Therefore, this
374                 // candidate applies to ALL outcomes.
375                 return false;
376             }
377         };
378
379         match test.kind {
380             // If we are performing a variant switch, then this
381             // informs variant patterns, but nothing else.
382             TestKind::Switch { adt_def: tested_adt_def } => {
383                 match *match_pair.pattern.kind {
384                     PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
385                         assert_eq!(adt_def, tested_adt_def);
386                         let new_candidate =
387                             self.candidate_after_variant_switch(match_pair_index,
388                                                                 adt_def,
389                                                                 variant_index,
390                                                                 subpatterns,
391                                                                 candidate);
392                         resulting_candidates[variant_index].push(new_candidate);
393                         true
394                     }
395                     _ => {
396                         false
397                     }
398                 }
399             }
400
401             // If we are performing a switch over integers, then this informs integer
402             // equality, but nothing else.
403             //
404             // FIXME(#29623) we could use TestKind::Range to rule
405             // things out here, in some cases.
406             TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => {
407                 match *match_pair.pattern.kind {
408                     PatternKind::Constant { ref value }
409                     if is_switch_ty(match_pair.pattern.ty) => {
410                         let index = indices[value];
411                         let new_candidate = self.candidate_without_match_pair(match_pair_index,
412                                                                               candidate);
413                         resulting_candidates[index].push(new_candidate);
414                         true
415                     }
416                     _ => {
417                         false
418                     }
419                 }
420             }
421
422             // If we are performing a length check, then this
423             // informs slice patterns, but nothing else.
424             TestKind::Len { .. } => {
425                 let pattern_test = self.test(&match_pair);
426                 match *match_pair.pattern.kind {
427                     PatternKind::Slice { .. } if pattern_test.kind == test.kind => {
428                         let mut new_candidate = candidate.clone();
429
430                         // Set up the MatchKind to simplify this like an array.
431                         new_candidate.match_pairs[match_pair_index]
432                                      .slice_len_checked = true;
433                         resulting_candidates[0].push(new_candidate);
434                         true
435                     }
436                     _ => false
437                 }
438             }
439
440             TestKind::Eq { .. } |
441             TestKind::Range { .. } => {
442                 // These are all binary tests.
443                 //
444                 // FIXME(#29623) we can be more clever here
445                 let pattern_test = self.test(&match_pair);
446                 if pattern_test.kind == test.kind {
447                     let new_candidate = self.candidate_without_match_pair(match_pair_index,
448                                                                           candidate);
449                     resulting_candidates[0].push(new_candidate);
450                     true
451                 } else {
452                     false
453                 }
454             }
455         }
456     }
457
458     fn candidate_without_match_pair<'pat>(&mut self,
459                                           match_pair_index: usize,
460                                           candidate: &Candidate<'pat, 'tcx>)
461                                           -> Candidate<'pat, 'tcx> {
462         let other_match_pairs =
463             candidate.match_pairs.iter()
464                                  .enumerate()
465                                  .filter(|&(index, _)| index != match_pair_index)
466                                  .map(|(_, mp)| mp.clone())
467                                  .collect();
468         Candidate {
469             match_pairs: other_match_pairs,
470             bindings: candidate.bindings.clone(),
471             guard: candidate.guard.clone(),
472             arm_index: candidate.arm_index,
473         }
474     }
475
476     fn candidate_after_variant_switch<'pat>(&mut self,
477                                             match_pair_index: usize,
478                                             adt_def: ty::AdtDef<'tcx>,
479                                             variant_index: usize,
480                                             subpatterns: &'pat [FieldPattern<'tcx>],
481                                             candidate: &Candidate<'pat, 'tcx>)
482                                             -> Candidate<'pat, 'tcx> {
483         let match_pair = &candidate.match_pairs[match_pair_index];
484
485         // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
486         // we want to create a set of derived match-patterns like
487         // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
488         let elem = ProjectionElem::Downcast(adt_def, variant_index);
489         let downcast_lvalue = match_pair.lvalue.clone().elem(elem); // `(x as Variant)`
490         let consequent_match_pairs =
491             subpatterns.iter()
492                        .map(|subpattern| {
493                            // e.g., `(x as Variant).0`
494                            let lvalue = downcast_lvalue.clone().field(subpattern.field,
495                                                                       subpattern.pattern.ty);
496                            // e.g., `(x as Variant).0 @ P1`
497                            MatchPair::new(lvalue, &subpattern.pattern)
498                        });
499
500         // In addition, we need all the other match pairs from the old candidate.
501         let other_match_pairs =
502             candidate.match_pairs.iter()
503                                  .enumerate()
504                                  .filter(|&(index, _)| index != match_pair_index)
505                                  .map(|(_, mp)| mp.clone());
506
507         let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
508
509         Candidate {
510             match_pairs: all_match_pairs,
511             bindings: candidate.bindings.clone(),
512             guard: candidate.guard.clone(),
513             arm_index: candidate.arm_index,
514         }
515     }
516
517     fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
518         self.hir.span_bug(match_pair.pattern.span,
519                           &format!("simplifyable pattern found: {:?}", match_pair.pattern))
520     }
521 }
522
523 fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
524     ty.is_integral() || ty.is_char() || ty.is_bool()
525 }