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.
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.
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.
19 use build::matches::{Candidate, MatchPair, Test, TestKind};
21 use rustc_data_structures::fx::FxHashMap;
22 use rustc_data_structures::bitvec::BitVector;
23 use rustc::middle::const_val::ConstVal;
24 use rustc::ty::{self, Ty};
26 use rustc::hir::RangeEnd;
28 use std::cmp::Ordering;
30 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
31 /// Identifies what test is needed to decide if `match_pair` is applicable.
33 /// It is a bug to call this with a simplifyable pattern.
34 pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
35 match *match_pair.pattern.kind {
36 PatternKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => {
38 span: match_pair.pattern.span,
39 kind: TestKind::Switch {
40 adt_def: adt_def.clone(),
41 variants: BitVector::new(self.hir.num_variants(adt_def)),
46 PatternKind::Constant { .. }
47 if is_switch_ty(match_pair.pattern.ty) => {
48 // for integers, we use a SwitchInt match, which allows
49 // us to handle more cases
51 span: match_pair.pattern.span,
52 kind: TestKind::SwitchInt {
53 switch_ty: match_pair.pattern.ty,
55 // these maps are empty to start; cases are
56 // added below in add_cases_to_switch
63 PatternKind::Constant { ref value } => {
65 span: match_pair.pattern.span,
68 ty: match_pair.pattern.ty.clone()
73 PatternKind::Range { ref lo, ref hi, ref end } => {
75 span: match_pair.pattern.span,
76 kind: TestKind::Range {
77 lo: Literal::Value { value: lo.clone() },
78 hi: Literal::Value { value: hi.clone() },
79 ty: match_pair.pattern.ty.clone(),
85 PatternKind::Slice { ref prefix, ref slice, ref suffix }
86 if !match_pair.slice_len_checked => {
87 let len = prefix.len() + suffix.len();
88 let op = if slice.is_some() {
94 span: match_pair.pattern.span,
95 kind: TestKind::Len { len: len as u64, op: op },
99 PatternKind::Array { .. } |
100 PatternKind::Slice { .. } |
102 PatternKind::Binding { .. } |
103 PatternKind::Leaf { .. } |
104 PatternKind::Deref { .. } => {
105 self.error_simplifyable(match_pair)
110 pub fn add_cases_to_switch<'pat>(&mut self,
111 test_lvalue: &Lvalue<'tcx>,
112 candidate: &Candidate<'pat, 'tcx>,
114 options: &mut Vec<ConstVal>,
115 indices: &mut FxHashMap<ConstVal, usize>)
118 let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
119 Some(match_pair) => match_pair,
120 _ => { return false; }
123 match *match_pair.pattern.kind {
124 PatternKind::Constant { ref value } => {
125 // if the lvalues match, the type should match
126 assert_eq!(match_pair.pattern.ty, switch_ty);
128 indices.entry(value.clone())
130 options.push(value.clone());
135 PatternKind::Variant { .. } => {
136 panic!("you should have called add_variants_to_switch instead!");
138 PatternKind::Range { .. } |
139 PatternKind::Slice { .. } |
140 PatternKind::Array { .. } |
142 PatternKind::Binding { .. } |
143 PatternKind::Leaf { .. } |
144 PatternKind::Deref { .. } => {
145 // don't know how to add these patterns to a switch
151 pub fn add_variants_to_switch<'pat>(&mut self,
152 test_lvalue: &Lvalue<'tcx>,
153 candidate: &Candidate<'pat, 'tcx>,
154 variants: &mut BitVector)
157 let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
158 Some(match_pair) => match_pair,
159 _ => { return false; }
162 match *match_pair.pattern.kind {
163 PatternKind::Variant { adt_def: _ , variant_index, .. } => {
164 // We have a pattern testing for variant `variant_index`
165 // set the corresponding index to true
166 variants.insert(variant_index);
170 // don't know how to add these patterns to a switch
176 /// Generates the code to perform a test.
177 pub fn perform_test(&mut self,
179 lvalue: &Lvalue<'tcx>,
182 let source_info = self.source_info(test.span);
184 TestKind::Switch { adt_def, ref variants } => {
185 let num_enum_variants = self.hir.num_variants(adt_def);
186 let mut otherwise_block = None;
187 let target_blocks: Vec<_> = (0..num_enum_variants).map(|i| {
188 if variants.contains(i) {
189 self.cfg.start_new_block()
191 if otherwise_block.is_none() {
192 otherwise_block = Some(self.cfg.start_new_block());
194 otherwise_block.unwrap()
197 debug!("num_enum_variants: {}, num tested variants: {}, variants: {:?}",
198 num_enum_variants, variants.iter().count(), variants);
199 self.cfg.terminate(block, source_info, TerminatorKind::Switch {
200 discr: lvalue.clone(),
202 targets: target_blocks.clone()
207 TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
208 let (targets, term) = match switch_ty.sty {
209 // If we're matching on boolean we can
210 // use the If TerminatorKind instead
212 assert!(options.len() > 0 && options.len() <= 2);
214 let (true_bb, else_bb) =
215 (self.cfg.start_new_block(),
216 self.cfg.start_new_block());
218 let targets = match &options[0] {
219 &ConstVal::Bool(true) => vec![true_bb, else_bb],
220 &ConstVal::Bool(false) => vec![else_bb, true_bb],
221 v => span_bug!(test.span, "expected boolean value but got {:?}", v)
226 cond: Operand::Consume(lvalue.clone()),
227 targets: (true_bb, else_bb)
232 // The switch may be inexhaustive so we
233 // add a catch all block
234 let otherwise = self.cfg.start_new_block();
235 let targets: Vec<_> =
237 .map(|_| self.cfg.start_new_block())
238 .chain(Some(otherwise))
242 TerminatorKind::SwitchInt {
243 discr: lvalue.clone(),
244 switch_ty: switch_ty,
245 values: options.clone(),
251 self.cfg.terminate(block, source_info, term);
255 TestKind::Eq { ref value, mut ty } => {
256 let mut val = Operand::Consume(lvalue.clone());
258 // If we're using b"..." as a pattern, we need to insert an
259 // unsizing coercion, as the byte string has the type &[u8; N].
260 let expect = if let ConstVal::ByteStr(ref bytes) = *value {
261 let tcx = self.hir.tcx();
263 // Unsize the lvalue to &[u8], too, if necessary.
264 if let ty::TyRef(region, mt) = ty.sty {
265 if let ty::TyArray(_, _) = mt.ty.sty {
266 ty = tcx.mk_imm_ref(region, tcx.mk_slice(tcx.types.u8));
267 let val_slice = self.temp(ty);
268 self.cfg.push_assign(block, source_info, &val_slice,
269 Rvalue::Cast(CastKind::Unsize, val, ty));
270 val = Operand::Consume(val_slice);
274 assert!(ty.is_slice());
276 let array_ty = tcx.mk_array(tcx.types.u8, bytes.len());
277 let array_ref = tcx.mk_imm_ref(tcx.mk_region(ty::ReStatic), array_ty);
278 let array = self.literal_operand(test.span, array_ref, Literal::Value {
282 let slice = self.temp(ty);
283 self.cfg.push_assign(block, source_info, &slice,
284 Rvalue::Cast(CastKind::Unsize, array, ty));
285 Operand::Consume(slice)
287 self.literal_operand(test.span, ty, Literal::Value {
292 // Use PartialEq::eq for &str and &[u8] slices, instead of BinOp::Eq.
293 let fail = self.cfg.start_new_block();
294 if let ty::TyRef(_, mt) = ty.sty {
295 assert!(ty.is_slice());
296 let eq_def_id = self.hir.tcx().lang_items.eq_trait().unwrap();
298 let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, &[ty]);
300 let bool_ty = self.hir.bool_ty();
301 let eq_result = self.temp(bool_ty);
302 let eq_block = self.cfg.start_new_block();
303 let cleanup = self.diverge_cleanup();
304 self.cfg.terminate(block, source_info, TerminatorKind::Call {
305 func: Operand::Constant(Constant {
310 args: vec![val, expect],
311 destination: Some((eq_result.clone(), eq_block)),
316 let block = self.cfg.start_new_block();
317 self.cfg.terminate(eq_block, source_info, TerminatorKind::If {
318 cond: Operand::Consume(eq_result),
319 targets: (block, fail),
324 let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
329 TestKind::Range { ref lo, ref hi, ty, ref end } => {
330 // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
331 let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
332 let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
333 let val = Operand::Consume(lvalue.clone());
335 let fail = self.cfg.start_new_block();
336 let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone());
337 let block = match *end {
338 RangeEnd::Included => self.compare(block, fail, test.span, BinOp::Le, val, hi),
339 RangeEnd::Excluded => self.compare(block, fail, test.span, BinOp::Lt, val, hi),
345 TestKind::Len { len, op } => {
346 let (usize_ty, bool_ty) = (self.hir.usize_ty(), self.hir.bool_ty());
347 let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
349 // actual = len(lvalue)
350 self.cfg.push_assign(block, source_info,
351 &actual, Rvalue::Len(lvalue.clone()));
354 let expected = self.push_usize(block, source_info, len);
356 // result = actual == expected OR result = actual < expected
357 self.cfg.push_assign(block, source_info, &result,
359 Operand::Consume(actual),
360 Operand::Consume(expected)));
362 // branch based on result
363 let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
364 self.cfg.start_new_block()];
365 self.cfg.terminate(block, source_info, TerminatorKind::If {
366 cond: Operand::Consume(result),
367 targets: (target_blocks[0], target_blocks[1])
375 fn compare(&mut self,
377 fail_block: BasicBlock,
381 right: Operand<'tcx>) -> BasicBlock {
382 let bool_ty = self.hir.bool_ty();
383 let result = self.temp(bool_ty);
385 // result = op(left, right)
386 let source_info = self.source_info(span);
387 self.cfg.push_assign(block, source_info, &result,
388 Rvalue::BinaryOp(op, left, right));
390 // branch based on result
391 let target_block = self.cfg.start_new_block();
392 self.cfg.terminate(block, source_info, TerminatorKind::If {
393 cond: Operand::Consume(result),
394 targets: (target_block, fail_block)
400 /// Given that we are performing `test` against `test_lvalue`,
401 /// this job sorts out what the status of `candidate` will be
402 /// after the test. The `resulting_candidates` vector stores, for
403 /// each possible outcome of `test`, a vector of the candidates
404 /// that will result. This fn should add a (possibly modified)
405 /// clone of candidate into `resulting_candidates` wherever
408 /// So, for example, if this candidate is `x @ Some(P0)` and the
409 /// test is a variant test, then we would add `(x as Option).0 @
410 /// P0` to the `resulting_candidates` entry corresponding to the
413 /// However, in some cases, the test may just not be relevant to
414 /// candidate. For example, suppose we are testing whether `foo.x == 22`,
415 /// but in one match arm we have `Foo { x: _, ... }`... in that case,
416 /// the test for what value `x` has has no particular relevance
417 /// to this candidate. In such cases, this function just returns false
418 /// without doing anything. This is used by the overall `match_candidates`
419 /// algorithm to structure the match as a whole. See `match_candidates` for
422 /// FIXME(#29623). In some cases, we have some tricky choices to
423 /// make. for example, if we are testing that `x == 22`, but the
424 /// candidate is `x @ 13..55`, what should we do? In the event
425 /// that the test is true, we know that the candidate applies, but
426 /// in the event of false, we don't know that it *doesn't*
427 /// apply. For now, we return false, indicate that the test does
428 /// not apply to this candidate, but it might be we can get
429 /// tighter match code if we do something a bit different.
430 pub fn sort_candidate<'pat>(&mut self,
431 test_lvalue: &Lvalue<'tcx>,
433 candidate: &Candidate<'pat, 'tcx>,
434 resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>])
436 // Find the match_pair for this lvalue (if any). At present,
437 // afaik, there can be at most one. (In the future, if we
438 // adopted a more general `@` operator, there might be more
439 // than one, but it'd be very unusual to have two sides that
440 // both require tests; you'd expect one side to be simplified
442 let tested_match_pair = candidate.match_pairs.iter()
444 .filter(|&(_, mp)| mp.lvalue == *test_lvalue)
446 let (match_pair_index, match_pair) = match tested_match_pair {
449 // We are not testing this lvalue. Therefore, this
450 // candidate applies to ALL outcomes.
455 match (&test.kind, &*match_pair.pattern.kind) {
456 // If we are performing a variant switch, then this
457 // informs variant patterns, but nothing else.
458 (&TestKind::Switch { adt_def: tested_adt_def, .. },
459 &PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. }) => {
460 assert_eq!(adt_def, tested_adt_def);
462 self.candidate_after_variant_switch(match_pair_index,
467 resulting_candidates[variant_index].push(new_candidate);
470 (&TestKind::Switch { .. }, _) => false,
472 // If we are performing a switch over integers, then this informs integer
473 // equality, but nothing else.
475 // FIXME(#29623) we could use PatternKind::Range to rule
476 // things out here, in some cases.
477 (&TestKind::SwitchInt { switch_ty: _, options: _, ref indices },
478 &PatternKind::Constant { ref value })
479 if is_switch_ty(match_pair.pattern.ty) => {
480 let index = indices[value];
481 let new_candidate = self.candidate_without_match_pair(match_pair_index,
483 resulting_candidates[index].push(new_candidate);
486 (&TestKind::SwitchInt { .. }, _) => false,
489 (&TestKind::Len { len: test_len, op: BinOp::Eq },
490 &PatternKind::Slice { ref prefix, ref slice, ref suffix }) => {
491 let pat_len = (prefix.len() + suffix.len()) as u64;
492 match (test_len.cmp(&pat_len), slice) {
493 (Ordering::Equal, &None) => {
494 // on true, min_len = len = $actual_length,
495 // on false, len != $actual_length
496 resulting_candidates[0].push(
497 self.candidate_after_slice_test(match_pair_index,
505 (Ordering::Less, _) => {
506 // test_len < pat_len. If $actual_len = test_len,
507 // then $actual_len < pat_len and we don't have
509 resulting_candidates[1].push(candidate.clone());
512 (Ordering::Equal, &Some(_)) | (Ordering::Greater, &Some(_)) => {
513 // This can match both if $actual_len = test_len >= pat_len,
514 // and if $actual_len > test_len. We can't advance.
517 (Ordering::Greater, &None) => {
518 // test_len != pat_len, so if $actual_len = test_len, then
519 // $actual_len != pat_len.
520 resulting_candidates[1].push(candidate.clone());
526 (&TestKind::Len { len: test_len, op: BinOp::Ge },
527 &PatternKind::Slice { ref prefix, ref slice, ref suffix }) => {
528 // the test is `$actual_len >= test_len`
529 let pat_len = (prefix.len() + suffix.len()) as u64;
530 match (test_len.cmp(&pat_len), slice) {
531 (Ordering::Equal, &Some(_)) => {
532 // $actual_len >= test_len = pat_len,
534 resulting_candidates[0].push(
535 self.candidate_after_slice_test(match_pair_index,
543 (Ordering::Less, _) | (Ordering::Equal, &None) => {
544 // test_len <= pat_len. If $actual_len < test_len,
545 // then it is also < pat_len, so the test passing is
546 // necessary (but insufficient).
547 resulting_candidates[0].push(candidate.clone());
550 (Ordering::Greater, &None) => {
551 // test_len > pat_len. If $actual_len >= test_len > pat_len,
552 // then we know we won't have a match.
553 resulting_candidates[1].push(candidate.clone());
556 (Ordering::Greater, &Some(_)) => {
557 // test_len < pat_len, and is therefore less
558 // strict. This can still go both ways.
564 (&TestKind::Eq { .. }, _) |
565 (&TestKind::Range { .. }, _) |
566 (&TestKind::Len { .. }, _) => {
567 // These are all binary tests.
569 // FIXME(#29623) we can be more clever here
570 let pattern_test = self.test(&match_pair);
571 if pattern_test.kind == test.kind {
572 let new_candidate = self.candidate_without_match_pair(match_pair_index,
574 resulting_candidates[0].push(new_candidate);
583 fn candidate_without_match_pair<'pat>(&mut self,
584 match_pair_index: usize,
585 candidate: &Candidate<'pat, 'tcx>)
586 -> Candidate<'pat, 'tcx> {
587 let other_match_pairs =
588 candidate.match_pairs.iter()
590 .filter(|&(index, _)| index != match_pair_index)
591 .map(|(_, mp)| mp.clone())
594 span: candidate.span,
595 match_pairs: other_match_pairs,
596 bindings: candidate.bindings.clone(),
597 guard: candidate.guard.clone(),
598 arm_index: candidate.arm_index,
602 fn candidate_after_slice_test<'pat>(&mut self,
603 match_pair_index: usize,
604 candidate: &Candidate<'pat, 'tcx>,
605 prefix: &'pat [Pattern<'tcx>],
606 opt_slice: Option<&'pat Pattern<'tcx>>,
607 suffix: &'pat [Pattern<'tcx>])
608 -> Candidate<'pat, 'tcx> {
609 let mut new_candidate =
610 self.candidate_without_match_pair(match_pair_index, candidate);
611 self.prefix_slice_suffix(
612 &mut new_candidate.match_pairs,
613 &candidate.match_pairs[match_pair_index].lvalue,
621 fn candidate_after_variant_switch<'pat>(&mut self,
622 match_pair_index: usize,
623 adt_def: &'tcx ty::AdtDef,
624 variant_index: usize,
625 subpatterns: &'pat [FieldPattern<'tcx>],
626 candidate: &Candidate<'pat, 'tcx>)
627 -> Candidate<'pat, 'tcx> {
628 let match_pair = &candidate.match_pairs[match_pair_index];
630 // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
631 // we want to create a set of derived match-patterns like
632 // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
633 let elem = ProjectionElem::Downcast(adt_def, variant_index);
634 let downcast_lvalue = match_pair.lvalue.clone().elem(elem); // `(x as Variant)`
635 let consequent_match_pairs =
638 // e.g., `(x as Variant).0`
639 let lvalue = downcast_lvalue.clone().field(subpattern.field,
640 subpattern.pattern.ty);
641 // e.g., `(x as Variant).0 @ P1`
642 MatchPair::new(lvalue, &subpattern.pattern)
645 // In addition, we need all the other match pairs from the old candidate.
646 let other_match_pairs =
647 candidate.match_pairs.iter()
649 .filter(|&(index, _)| index != match_pair_index)
650 .map(|(_, mp)| mp.clone());
652 let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
655 span: candidate.span,
656 match_pairs: all_match_pairs,
657 bindings: candidate.bindings.clone(),
658 guard: candidate.guard.clone(),
659 arm_index: candidate.arm_index,
663 fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
664 span_bug!(match_pair.pattern.span,
665 "simplifyable pattern found: {:?}",
670 fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
671 ty.is_integral() || ty.is_char() || ty.is_bool()