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::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;
27 impl<'a,'tcx> Builder<'a,'tcx> {
28 /// Identifies what test is needed to decide if `match_pair` is applicable.
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: _ } => {
35 span: match_pair.pattern.span,
36 kind: TestKind::Switch { adt_def: adt_def.clone() },
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
45 span: match_pair.pattern.span,
46 kind: TestKind::SwitchInt {
47 switch_ty: match_pair.pattern.ty,
49 // these maps are empty to start; cases are
50 // added below in add_cases_to_switch
52 indices: FnvHashMap(),
57 PatternKind::Constant { ref value } => {
59 span: match_pair.pattern.span,
62 ty: match_pair.pattern.ty.clone()
67 PatternKind::Range { ref lo, ref hi } => {
69 span: match_pair.pattern.span,
70 kind: TestKind::Range {
73 ty: match_pair.pattern.ty.clone(),
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() {
87 span: match_pair.pattern.span,
88 kind: TestKind::Len { len: len as u64, op: op },
92 PatternKind::Array { .. } |
93 PatternKind::Slice { .. } |
95 PatternKind::Binding { .. } |
96 PatternKind::Leaf { .. } |
97 PatternKind::Deref { .. } => {
98 self.error_simplifyable(match_pair)
103 pub fn add_cases_to_switch<'pat>(&mut self,
104 test_lvalue: &Lvalue<'tcx>,
105 candidate: &Candidate<'pat, 'tcx>,
107 options: &mut Vec<ConstVal>,
108 indices: &mut FnvHashMap<ConstVal, usize>)
111 let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
112 Some(match_pair) => match_pair,
113 _ => { return false; }
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);
121 indices.entry(value.clone())
123 options.push(value.clone());
129 PatternKind::Range { .. } |
130 PatternKind::Variant { .. } |
131 PatternKind::Slice { .. } |
132 PatternKind::Array { .. } |
134 PatternKind::Binding { .. } |
135 PatternKind::Leaf { .. } |
136 PatternKind::Deref { .. } => {
137 // don't know how to add these patterns to a switch
143 /// Generates the code to perform a test.
144 pub fn perform_test(&mut self,
146 lvalue: &Lvalue<'tcx>,
149 let scope_id = self.innermost_scope_id();
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())
156 self.cfg.terminate(block, TerminatorKind::Switch {
157 discr: lvalue.clone(),
159 targets: target_blocks.clone()
164 TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
165 let otherwise = self.cfg.start_new_block();
166 let targets: Vec<_> =
168 .map(|_| self.cfg.start_new_block())
169 .chain(Some(otherwise))
171 self.cfg.terminate(block, TerminatorKind::SwitchInt {
172 discr: lvalue.clone(),
173 switch_ty: switch_ty,
174 values: options.clone(),
175 targets: targets.clone(),
180 TestKind::Eq { ref value, mut ty } => {
181 let mut val = Operand::Consume(lvalue.clone());
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();
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);
199 assert!(ty.is_slice());
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 {
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)
212 self.literal_operand(test.span, ty, Literal::Value {
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();
223 let (mty, method) = self.hir.trait_method(eq_def_id, "eq", ty, vec![ty]);
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 {
235 args: vec![val, expect],
236 destination: Some((eq_result.clone(), eq_block)),
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),
249 let block = self.compare(block, fail, test.span, BinOp::Eq, expect, val);
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());
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);
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));
271 // actual = len(lvalue)
272 self.cfg.push_assign(block, scope_id, test.span, &actual, Rvalue::Len(lvalue.clone()));
275 let expected = self.push_usize(block, scope_id, test.span, len);
277 // result = actual == expected OR result = actual < expected
278 self.cfg.push_assign(block,
283 Operand::Consume(actual),
284 Operand::Consume(expected)));
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])
299 fn compare(&mut self,
301 fail_block: BasicBlock,
305 right: Operand<'tcx>) -> BasicBlock {
306 let bool_ty = self.hir.bool_ty();
307 let result = self.temp(bool_ty);
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));
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)
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
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
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
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>,
357 candidate: &Candidate<'pat, 'tcx>,
358 resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>])
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
366 let tested_match_pair = candidate.match_pairs.iter()
368 .filter(|&(_, mp)| mp.lvalue == *test_lvalue)
370 let (match_pair_index, match_pair) = match tested_match_pair {
373 // We are not testing this lvalue. Therefore, this
374 // candidate applies to ALL outcomes.
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);
387 self.candidate_after_variant_switch(match_pair_index,
392 resulting_candidates[variant_index].push(new_candidate);
401 // If we are performing a switch over integers, then this informs integer
402 // equality, but nothing else.
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,
413 resulting_candidates[index].push(new_candidate);
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();
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);
440 TestKind::Eq { .. } |
441 TestKind::Range { .. } => {
442 // These are all binary tests.
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,
449 resulting_candidates[0].push(new_candidate);
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()
465 .filter(|&(index, _)| index != match_pair_index)
466 .map(|(_, mp)| mp.clone())
469 match_pairs: other_match_pairs,
470 bindings: candidate.bindings.clone(),
471 guard: candidate.guard.clone(),
472 arm_index: candidate.arm_index,
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];
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 =
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)
500 // In addition, we need all the other match pairs from the old candidate.
501 let other_match_pairs =
502 candidate.match_pairs.iter()
504 .filter(|&(index, _)| index != match_pair_index)
505 .map(|(_, mp)| mp.clone());
507 let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
510 match_pairs: all_match_pairs,
511 bindings: candidate.bindings.clone(),
512 guard: candidate.guard.clone(),
513 arm_index: candidate.arm_index,
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))
523 fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
524 ty.is_integral() || ty.is_char() || ty.is_bool()