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