Introduce a SwitchInt
and restructure pattern matching to collect
integers and characters into one master switch.
This commit is contained in:
parent
8fa8684b4c
commit
3e6b4545f9
6 changed files with 319 additions and 74 deletions
|
@ -39,6 +39,7 @@ use std::borrow::{Cow, IntoCow};
|
||||||
use std::num::wrapping::OverflowingOps;
|
use std::num::wrapping::OverflowingOps;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::hash_map::Entry::Vacant;
|
use std::collections::hash_map::Entry::Vacant;
|
||||||
|
use std::hash;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use std::{i8, i16, i32, i64, u8, u16, u32, u64};
|
use std::{i8, i16, i32, i64, u8, u16, u32, u64};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -257,6 +258,22 @@ pub enum ConstVal {
|
||||||
Function(DefId),
|
Function(DefId),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl hash::Hash for ConstVal {
|
||||||
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
|
match *self {
|
||||||
|
Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state),
|
||||||
|
Int(a) => a.hash(state),
|
||||||
|
Uint(a) => a.hash(state),
|
||||||
|
Str(ref a) => a.hash(state),
|
||||||
|
ByteStr(ref a) => a.hash(state),
|
||||||
|
Bool(a) => a.hash(state),
|
||||||
|
Struct(a) => a.hash(state),
|
||||||
|
Tuple(a) => a.hash(state),
|
||||||
|
Function(a) => a.hash(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Note that equality for `ConstVal` means that the it is the same
|
/// Note that equality for `ConstVal` means that the it is the same
|
||||||
/// constant, not that the rust values are equal. In particular, `NaN
|
/// constant, not that the rust values are equal. In particular, `NaN
|
||||||
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
|
/// == NaN` (at least if it's the same NaN; distinct encodings for NaN
|
||||||
|
@ -278,6 +295,8 @@ impl PartialEq for ConstVal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for ConstVal { }
|
||||||
|
|
||||||
impl ConstVal {
|
impl ConstVal {
|
||||||
pub fn description(&self) -> &'static str {
|
pub fn description(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
use build::{BlockAnd, Builder};
|
use build::{BlockAnd, Builder};
|
||||||
use repr::*;
|
use repr::*;
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
use rustc::middle::const_eval::ConstVal;
|
||||||
use rustc::middle::region::CodeExtent;
|
use rustc::middle::region::CodeExtent;
|
||||||
use rustc::middle::ty::{AdtDef, Ty};
|
use rustc::middle::ty::{AdtDef, Ty};
|
||||||
use hair::*;
|
use hair::*;
|
||||||
|
@ -241,6 +243,13 @@ enum TestKind<'tcx> {
|
||||||
adt_def: AdtDef<'tcx>,
|
adt_def: AdtDef<'tcx>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// test the branches of enum
|
||||||
|
SwitchInt {
|
||||||
|
switch_ty: Ty<'tcx>,
|
||||||
|
options: Vec<ConstVal>,
|
||||||
|
indices: FnvHashMap<ConstVal, usize>,
|
||||||
|
},
|
||||||
|
|
||||||
// test for equality
|
// test for equality
|
||||||
Eq {
|
Eq {
|
||||||
value: Literal<'tcx>,
|
value: Literal<'tcx>,
|
||||||
|
@ -315,20 +324,36 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
|
|
||||||
// otherwise, extract the next match pair and construct tests
|
// otherwise, extract the next match pair and construct tests
|
||||||
let match_pair = &candidates.last().unwrap().match_pairs[0];
|
let match_pair = &candidates.last().unwrap().match_pairs[0];
|
||||||
let test = self.test(match_pair);
|
let mut test = self.test(match_pair);
|
||||||
|
|
||||||
|
// most of the time, the test to perform is simply a function
|
||||||
|
// of the main candidate; but for a test like SwitchInt, we
|
||||||
|
// may want to add cases based on the candidates that are
|
||||||
|
// available
|
||||||
|
match test.kind {
|
||||||
|
TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
|
||||||
|
for candidate in &candidates {
|
||||||
|
self.add_cases_to_switch(&match_pair.lvalue,
|
||||||
|
candidate,
|
||||||
|
switch_ty,
|
||||||
|
options,
|
||||||
|
indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
|
||||||
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
|
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
|
||||||
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
|
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
|
||||||
|
|
||||||
for (outcome, mut target_block) in target_blocks.into_iter().enumerate() {
|
for (outcome, target_block) in target_blocks.into_iter().enumerate() {
|
||||||
let applicable_candidates: Vec<Candidate<'tcx>> =
|
let applicable_candidates: Vec<Candidate<'tcx>> =
|
||||||
candidates.iter()
|
candidates.iter()
|
||||||
.filter_map(|candidate| {
|
.filter_map(|candidate| {
|
||||||
unpack!(target_block =
|
self.candidate_under_assumption(&match_pair.lvalue,
|
||||||
self.candidate_under_assumption(target_block,
|
&test.kind,
|
||||||
&match_pair.lvalue,
|
outcome,
|
||||||
&test.kind,
|
candidate)
|
||||||
outcome,
|
|
||||||
candidate))
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
self.match_candidates(span, arm_blocks, applicable_candidates, target_block);
|
self.match_candidates(span, arm_blocks, applicable_candidates, target_block);
|
||||||
|
|
|
@ -15,10 +15,13 @@
|
||||||
// identify what tests are needed, perform the tests, and then filter
|
// identify what tests are needed, perform the tests, and then filter
|
||||||
// the candidates based on the result.
|
// the candidates based on the result.
|
||||||
|
|
||||||
use build::{BlockAnd, Builder};
|
use build::Builder;
|
||||||
use build::matches::{Candidate, MatchPair, Test, TestKind};
|
use build::matches::{Candidate, MatchPair, Test, TestKind};
|
||||||
use hair::*;
|
use hair::*;
|
||||||
use repr::*;
|
use repr::*;
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
use rustc::middle::const_eval::ConstVal;
|
||||||
|
use rustc::middle::ty::Ty;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
|
|
||||||
impl<'a,'tcx> Builder<'a,'tcx> {
|
impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
|
@ -34,13 +37,31 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PatternKind::Constant { value: Literal::Value { .. } }
|
||||||
|
if is_switch_ty(match_pair.pattern.ty) => {
|
||||||
|
// for integers, we use a SwitchInt match, which allows
|
||||||
|
// us to handle more cases
|
||||||
|
Test {
|
||||||
|
span: match_pair.pattern.span,
|
||||||
|
kind: TestKind::SwitchInt {
|
||||||
|
switch_ty: match_pair.pattern.ty,
|
||||||
|
|
||||||
|
// these maps are empty to start; cases are
|
||||||
|
// added below in add_cases_to_switch
|
||||||
|
options: vec![],
|
||||||
|
indices: FnvHashMap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PatternKind::Constant { ref value } => {
|
PatternKind::Constant { ref value } => {
|
||||||
|
// for other types, we use an equality comparison
|
||||||
Test {
|
Test {
|
||||||
span: match_pair.pattern.span,
|
span: match_pair.pattern.span,
|
||||||
kind: TestKind::Eq {
|
kind: TestKind::Eq {
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
ty: match_pair.pattern.ty.clone(),
|
ty: match_pair.pattern.ty.clone(),
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,13 +99,52 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_cases_to_switch(&mut self,
|
||||||
|
test_lvalue: &Lvalue<'tcx>,
|
||||||
|
candidate: &Candidate<'tcx>,
|
||||||
|
switch_ty: Ty<'tcx>,
|
||||||
|
options: &mut Vec<ConstVal>,
|
||||||
|
indices: &mut FnvHashMap<ConstVal, usize>)
|
||||||
|
{
|
||||||
|
let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
|
||||||
|
Some(match_pair) => match_pair,
|
||||||
|
_ => { return; }
|
||||||
|
};
|
||||||
|
|
||||||
|
match match_pair.pattern.kind {
|
||||||
|
PatternKind::Constant { value: Literal::Value { ref value } } => {
|
||||||
|
// if the lvalues match, the type should match
|
||||||
|
assert_eq!(match_pair.pattern.ty, switch_ty);
|
||||||
|
|
||||||
|
indices.entry(value.clone())
|
||||||
|
.or_insert_with(|| {
|
||||||
|
options.push(value.clone());
|
||||||
|
options.len() - 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Range { .. } => {
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Constant { .. } |
|
||||||
|
PatternKind::Variant { .. } |
|
||||||
|
PatternKind::Slice { .. } |
|
||||||
|
PatternKind::Array { .. } |
|
||||||
|
PatternKind::Wild |
|
||||||
|
PatternKind::Binding { .. } |
|
||||||
|
PatternKind::Leaf { .. } |
|
||||||
|
PatternKind::Deref { .. } => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates the code to perform a test.
|
/// Generates the code to perform a test.
|
||||||
pub fn perform_test(&mut self,
|
pub fn perform_test(&mut self,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
lvalue: &Lvalue<'tcx>,
|
lvalue: &Lvalue<'tcx>,
|
||||||
test: &Test<'tcx>)
|
test: &Test<'tcx>)
|
||||||
-> Vec<BasicBlock> {
|
-> Vec<BasicBlock> {
|
||||||
match test.kind.clone() {
|
match test.kind {
|
||||||
TestKind::Switch { adt_def } => {
|
TestKind::Switch { adt_def } => {
|
||||||
let num_enum_variants = self.hir.num_variants(adt_def);
|
let num_enum_variants = self.hir.num_variants(adt_def);
|
||||||
let target_blocks: Vec<_> =
|
let target_blocks: Vec<_> =
|
||||||
|
@ -98,18 +158,34 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
target_blocks
|
target_blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
TestKind::Eq { value, ty } => {
|
TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
|
||||||
|
let otherwise = self.cfg.start_new_block();
|
||||||
|
let targets: Vec<_> =
|
||||||
|
options.iter()
|
||||||
|
.map(|_| self.cfg.start_new_block())
|
||||||
|
.chain(Some(otherwise))
|
||||||
|
.collect();
|
||||||
|
self.cfg.terminate(block, Terminator::SwitchInt {
|
||||||
|
discr: lvalue.clone(),
|
||||||
|
switch_ty: switch_ty,
|
||||||
|
values: options.clone(),
|
||||||
|
targets: targets.clone(),
|
||||||
|
});
|
||||||
|
targets
|
||||||
|
}
|
||||||
|
|
||||||
|
TestKind::Eq { ref value, ty } => {
|
||||||
// call PartialEq::eq(discrim, constant)
|
// call PartialEq::eq(discrim, constant)
|
||||||
let constant = self.literal_operand(test.span, ty.clone(), value);
|
let constant = self.literal_operand(test.span, ty.clone(), value.clone());
|
||||||
let item_ref = self.hir.partial_eq(ty);
|
let item_ref = self.hir.partial_eq(ty);
|
||||||
self.call_comparison_fn(block, test.span, item_ref,
|
self.call_comparison_fn(block, test.span, item_ref,
|
||||||
Operand::Consume(lvalue.clone()), constant)
|
Operand::Consume(lvalue.clone()), constant)
|
||||||
}
|
}
|
||||||
|
|
||||||
TestKind::Range { lo, hi, ty } => {
|
TestKind::Range { ref lo, ref hi, ty } => {
|
||||||
// Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
|
// Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
|
||||||
let lo = self.literal_operand(test.span, ty.clone(), lo);
|
let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
|
||||||
let hi = self.literal_operand(test.span, ty.clone(), hi);
|
let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
|
||||||
let item_ref = self.hir.partial_le(ty);
|
let item_ref = self.hir.partial_le(ty);
|
||||||
|
|
||||||
let lo_blocks = self.call_comparison_fn(block,
|
let lo_blocks = self.call_comparison_fn(block,
|
||||||
|
@ -207,34 +283,31 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
/// were `Ok`, we would return `Some([x.0.downcast<Ok>.0 @ P1, x.1
|
/// were `Ok`, we would return `Some([x.0.downcast<Ok>.0 @ P1, x.1
|
||||||
/// @ 22])`.
|
/// @ 22])`.
|
||||||
pub fn candidate_under_assumption(&mut self,
|
pub fn candidate_under_assumption(&mut self,
|
||||||
mut block: BasicBlock,
|
|
||||||
test_lvalue: &Lvalue<'tcx>,
|
test_lvalue: &Lvalue<'tcx>,
|
||||||
test_kind: &TestKind<'tcx>,
|
test_kind: &TestKind<'tcx>,
|
||||||
test_outcome: usize,
|
test_outcome: usize,
|
||||||
candidate: &Candidate<'tcx>)
|
candidate: &Candidate<'tcx>)
|
||||||
-> BlockAnd<Option<Candidate<'tcx>>> {
|
-> Option<Candidate<'tcx>> {
|
||||||
let candidate = candidate.clone();
|
let candidate = candidate.clone();
|
||||||
let match_pairs = candidate.match_pairs;
|
let match_pairs = candidate.match_pairs;
|
||||||
let result = unpack!(block = self.match_pairs_under_assumption(block,
|
let result = self.match_pairs_under_assumption(test_lvalue,
|
||||||
test_lvalue,
|
test_kind,
|
||||||
test_kind,
|
test_outcome,
|
||||||
test_outcome,
|
match_pairs);
|
||||||
match_pairs));
|
match result {
|
||||||
block.and(match result {
|
|
||||||
Some(match_pairs) => Some(Candidate { match_pairs: match_pairs, ..candidate }),
|
Some(match_pairs) => Some(Candidate { match_pairs: match_pairs, ..candidate }),
|
||||||
None => None,
|
None => None,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper for candidate_under_assumption that does the actual
|
/// Helper for candidate_under_assumption that does the actual
|
||||||
/// work of transforming the list of match pairs.
|
/// work of transforming the list of match pairs.
|
||||||
fn match_pairs_under_assumption(&mut self,
|
fn match_pairs_under_assumption(&mut self,
|
||||||
mut block: BasicBlock,
|
|
||||||
test_lvalue: &Lvalue<'tcx>,
|
test_lvalue: &Lvalue<'tcx>,
|
||||||
test_kind: &TestKind<'tcx>,
|
test_kind: &TestKind<'tcx>,
|
||||||
test_outcome: usize,
|
test_outcome: usize,
|
||||||
match_pairs: Vec<MatchPair<'tcx>>)
|
match_pairs: Vec<MatchPair<'tcx>>)
|
||||||
-> BlockAnd<Option<Vec<MatchPair<'tcx>>>> {
|
-> Option<Vec<MatchPair<'tcx>>> {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
|
|
||||||
for match_pair in match_pairs {
|
for match_pair in match_pairs {
|
||||||
|
@ -245,30 +318,17 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let desired_test = self.test(&match_pair);
|
// if this test doesn't tell us anything about this match-pair, then hang onto it.
|
||||||
|
if !self.test_informs_match_pair(&match_pair, test_kind, test_outcome) {
|
||||||
if *test_kind != desired_test.kind {
|
|
||||||
// if the match pair wants to (e.g.) test for
|
|
||||||
// equality against some particular constant, but
|
|
||||||
// we did a switch, then we can't say whether it
|
|
||||||
// matches or not, so we still have to include it
|
|
||||||
// as a possibility.
|
|
||||||
//
|
|
||||||
// For example, we have a constant `FOO:
|
|
||||||
// Option<i32> = Some(22)`, and `match_pair` is `x
|
|
||||||
// @ FOO`, but we did a switch on the variant
|
|
||||||
// (`Some` vs `None`). (OK, in principle this
|
|
||||||
// could tell us something, but we're not that
|
|
||||||
// smart yet to actually dig into the constant
|
|
||||||
// itself)
|
|
||||||
result.push(match_pair);
|
result.push(match_pair);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// otherwise, build up the consequence match pairs
|
||||||
let opt_consequent_match_pairs =
|
let opt_consequent_match_pairs =
|
||||||
unpack!(block = self.consequent_match_pairs_under_assumption(block,
|
self.consequent_match_pairs_under_assumption(match_pair,
|
||||||
match_pair,
|
test_kind,
|
||||||
test_outcome));
|
test_outcome);
|
||||||
match opt_consequent_match_pairs {
|
match opt_consequent_match_pairs {
|
||||||
None => {
|
None => {
|
||||||
// Right kind of test, but wrong outcome. That
|
// Right kind of test, but wrong outcome. That
|
||||||
|
@ -276,7 +336,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
// inapplicable, since the candidate is only
|
// inapplicable, since the candidate is only
|
||||||
// applicable if all of its match-pairs apply (and
|
// applicable if all of its match-pairs apply (and
|
||||||
// this one doesn't).
|
// this one doesn't).
|
||||||
return block.and(None);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(consequent_match_pairs) => {
|
Some(consequent_match_pairs) => {
|
||||||
|
@ -285,23 +345,122 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block.and(Some(result))
|
|
||||||
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identifies what test is needed to decide if `match_pair` is applicable.
|
/// Given that we executed `test` to `match_pair.lvalue` with
|
||||||
|
/// outcome `test_outcome`, does that tell us anything about
|
||||||
|
/// whether `match_pair` applies?
|
||||||
///
|
///
|
||||||
/// It is a bug to call this with a simplifyable pattern.
|
/// Often it does not. For example, if we are testing whether
|
||||||
|
/// the discriminant equals 4, and we find that it does not,
|
||||||
|
/// but the `match_pair` is testing if the discriminant equals 5,
|
||||||
|
/// that does not help us.
|
||||||
|
fn test_informs_match_pair(&mut self,
|
||||||
|
match_pair: &MatchPair<'tcx>,
|
||||||
|
test_kind: &TestKind<'tcx>,
|
||||||
|
_test_outcome: usize)
|
||||||
|
-> bool {
|
||||||
|
match match_pair.pattern.kind {
|
||||||
|
PatternKind::Variant { .. } => {
|
||||||
|
match *test_kind {
|
||||||
|
TestKind::Switch { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Constant { value: Literal::Value { .. } }
|
||||||
|
if is_switch_ty(match_pair.pattern.ty) => {
|
||||||
|
match *test_kind {
|
||||||
|
TestKind::SwitchInt { .. } => true,
|
||||||
|
|
||||||
|
// Did not do an integer equality test (which is always a SwitchInt).
|
||||||
|
// So we learned nothing relevant to this match-pair.
|
||||||
|
//
|
||||||
|
// TODO we could use TestKind::Range to rule
|
||||||
|
// things out here, in some cases.
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Constant { .. } |
|
||||||
|
PatternKind::Range { .. } |
|
||||||
|
PatternKind::Slice { .. } => {
|
||||||
|
let pattern_test = self.test(&match_pair);
|
||||||
|
if pattern_test.kind == *test_kind {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// TODO in all 3 cases, we could sometimes do
|
||||||
|
// better here. For example, if we are checking
|
||||||
|
// whether the value is equal to X, and we find
|
||||||
|
// that it is, that (may) imply value is not equal
|
||||||
|
// to Y. Or, if the range tested is `3..5`, and
|
||||||
|
// our range is `4..5`, then we know that our
|
||||||
|
// range also does not apply. Similarly, if we
|
||||||
|
// test that length is >= 5, and it fails, we also
|
||||||
|
// know that length is not >= 7. etc.
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Array { .. } |
|
||||||
|
PatternKind::Wild |
|
||||||
|
PatternKind::Binding { .. } |
|
||||||
|
PatternKind::Leaf { .. } |
|
||||||
|
PatternKind::Deref { .. } => {
|
||||||
|
self.error_simplifyable(&match_pair)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given that we executed `test` with outcome `test_outcome`,
|
||||||
|
/// what are the resulting match pairs? This can return either:
|
||||||
|
///
|
||||||
|
/// - None, meaning that the test indicated that this outcome
|
||||||
|
/// means that this match-pair is not the current one for the
|
||||||
|
/// current discriminant (which rules out the enclosing
|
||||||
|
/// candidate);
|
||||||
|
/// - Some(...), meaning that either the test didn't tell us whether this
|
||||||
|
/// match-pair is correct or not, or that we DID match and now have
|
||||||
|
/// subsequent matches to perform.
|
||||||
|
///
|
||||||
|
/// As an example, consider:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// match option {
|
||||||
|
/// Ok(<pattern>) => ...,
|
||||||
|
/// Err(_) => ...,
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Suppose that the `test` is a `Switch` and the outcome is
|
||||||
|
/// `Ok`. Then in that case, the first arm will have a match-pair
|
||||||
|
/// of `option @ Ok(<pattern>)`. In that case we will return
|
||||||
|
/// `Some(vec![(option as Ok) @ <pattern>])`. The `Some` reuslt
|
||||||
|
/// indicates that the match-pair still applies, and we now have
|
||||||
|
/// to test `(option as Ok) @ <pattern>`.
|
||||||
|
///
|
||||||
|
/// On the second arm, a `None` will be returned, because if we
|
||||||
|
/// observed that `option` has the discriminant `Ok`, then the
|
||||||
|
/// second arm cannot apply.
|
||||||
pub fn consequent_match_pairs_under_assumption(&mut self,
|
pub fn consequent_match_pairs_under_assumption(&mut self,
|
||||||
mut block: BasicBlock,
|
|
||||||
match_pair: MatchPair<'tcx>,
|
match_pair: MatchPair<'tcx>,
|
||||||
|
test_kind: &TestKind<'tcx>,
|
||||||
test_outcome: usize)
|
test_outcome: usize)
|
||||||
-> BlockAnd<Option<Vec<MatchPair<'tcx>>>> {
|
-> Option<Vec<MatchPair<'tcx>>> {
|
||||||
match match_pair.pattern.kind {
|
match match_pair.pattern.kind {
|
||||||
PatternKind::Variant { adt_def, variant_index, subpatterns } => {
|
PatternKind::Variant { adt_def, variant_index, subpatterns } => {
|
||||||
|
assert!(match *test_kind { TestKind::Switch { .. } => true,
|
||||||
|
_ => false });
|
||||||
|
|
||||||
if test_outcome != variant_index {
|
if test_outcome != variant_index {
|
||||||
return block.and(None);
|
return None; // Tested, but found the wrong variant.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Correct variant. Extract the subitems and match
|
||||||
|
// those. The lvalue goes gets downcast, so
|
||||||
|
// e.g. `foo.bar` becomes `foo.bar as Variant`.
|
||||||
let elem = ProjectionElem::Downcast(adt_def, variant_index);
|
let elem = ProjectionElem::Downcast(adt_def, variant_index);
|
||||||
let downcast_lvalue = match_pair.lvalue.clone().elem(elem);
|
let downcast_lvalue = match_pair.lvalue.clone().elem(elem);
|
||||||
let consequent_match_pairs =
|
let consequent_match_pairs =
|
||||||
|
@ -313,33 +472,37 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
self.match_pair(lvalue, subpattern.pattern)
|
self.match_pair(lvalue, subpattern.pattern)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
block.and(Some(consequent_match_pairs))
|
Some(consequent_match_pairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
PatternKind::Constant { .. } |
|
PatternKind::Constant { value: Literal::Value { ref value } }
|
||||||
PatternKind::Range { .. } => {
|
if is_switch_ty(match_pair.pattern.ty) => {
|
||||||
// these are boolean tests: if we are on the 0th
|
match *test_kind {
|
||||||
// successor, then they passed, and otherwise they
|
TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => {
|
||||||
// failed, but there are never any more tests to come.
|
let index = indices[value];
|
||||||
if test_outcome == 0 {
|
if index == test_outcome {
|
||||||
block.and(Some(vec![]))
|
Some(vec![]) // this value, nothing left to test
|
||||||
} else {
|
} else {
|
||||||
block.and(None)
|
None // some other value, candidate is inapplicable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
self.hir.span_bug(
|
||||||
|
match_pair.pattern.span,
|
||||||
|
&format!("did a switch-int, but value {:?} not found in cases",
|
||||||
|
value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PatternKind::Slice { prefix, slice, suffix } => {
|
PatternKind::Constant { .. } |
|
||||||
|
PatternKind::Range { .. } |
|
||||||
|
PatternKind::Slice { .. } => {
|
||||||
if test_outcome == 0 {
|
if test_outcome == 0 {
|
||||||
let mut consequent_match_pairs = vec![];
|
Some(vec![])
|
||||||
unpack!(block = self.prefix_suffix_slice(&mut consequent_match_pairs,
|
|
||||||
block,
|
|
||||||
match_pair.lvalue,
|
|
||||||
prefix,
|
|
||||||
slice,
|
|
||||||
suffix));
|
|
||||||
block.and(Some(consequent_match_pairs))
|
|
||||||
} else {
|
} else {
|
||||||
block.and(None)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,3 +521,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||||
&format!("simplifyable pattern found: {:?}", match_pair.pattern))
|
&format!("simplifyable pattern found: {:?}", match_pair.pattern))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
|
||||||
|
ty.is_integral() || ty.is_char()
|
||||||
|
}
|
||||||
|
|
|
@ -251,6 +251,26 @@ pub enum Terminator<'tcx> {
|
||||||
targets: Vec<BasicBlock>,
|
targets: Vec<BasicBlock>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// operand evaluates to an integer; jump depending on its value
|
||||||
|
/// to one of the targets, and otherwise fallback to `otherwise`
|
||||||
|
SwitchInt {
|
||||||
|
/// discriminant value being tested
|
||||||
|
discr: Lvalue<'tcx>,
|
||||||
|
|
||||||
|
/// type of value being tested
|
||||||
|
switch_ty: Ty<'tcx>,
|
||||||
|
|
||||||
|
/// Possible values. The locations to branch to in each case
|
||||||
|
/// are found in the corresponding indices from the `targets` vector.
|
||||||
|
values: Vec<ConstVal>,
|
||||||
|
|
||||||
|
/// Possible branch sites. The length of this vector should be
|
||||||
|
/// equal to the length of the `values` vector plus 1 -- the
|
||||||
|
/// extra item is the block to branch to if none of the values
|
||||||
|
/// fit.
|
||||||
|
targets: Vec<BasicBlock>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Indicates that the last statement in the block panics, aborts,
|
/// Indicates that the last statement in the block panics, aborts,
|
||||||
/// etc. No successors. This terminator appears on exactly one
|
/// etc. No successors. This terminator appears on exactly one
|
||||||
/// basic block which we create in advance. However, during
|
/// basic block which we create in advance. However, during
|
||||||
|
@ -280,7 +300,8 @@ impl<'tcx> Terminator<'tcx> {
|
||||||
Goto { target: ref b } => slice::ref_slice(b),
|
Goto { target: ref b } => slice::ref_slice(b),
|
||||||
Panic { target: ref b } => slice::ref_slice(b),
|
Panic { target: ref b } => slice::ref_slice(b),
|
||||||
If { cond: _, targets: ref b } => b,
|
If { cond: _, targets: ref b } => b,
|
||||||
Switch { discr: _, adt_def: _, targets: ref b } => b,
|
Switch { targets: ref b, .. } => b,
|
||||||
|
SwitchInt { targets: ref b, .. } => b,
|
||||||
Diverge => &[],
|
Diverge => &[],
|
||||||
Return => &[],
|
Return => &[],
|
||||||
Call { data: _, targets: ref b } => b,
|
Call { data: _, targets: ref b } => b,
|
||||||
|
@ -321,6 +342,8 @@ impl<'tcx> Debug for Terminator<'tcx> {
|
||||||
write!(fmt, "if({:?}) -> {:?}", lv, targets),
|
write!(fmt, "if({:?}) -> {:?}", lv, targets),
|
||||||
Switch { discr: ref lv, adt_def: _, ref targets } =>
|
Switch { discr: ref lv, adt_def: _, ref targets } =>
|
||||||
write!(fmt, "switch({:?}) -> {:?}", lv, targets),
|
write!(fmt, "switch({:?}) -> {:?}", lv, targets),
|
||||||
|
SwitchInt { discr: ref lv, switch_ty: _, ref values, ref targets } =>
|
||||||
|
write!(fmt, "switchInt({:?}, {:?}) -> {:?}", lv, values, targets),
|
||||||
Diverge =>
|
Diverge =>
|
||||||
write!(fmt, "diverge"),
|
write!(fmt, "diverge"),
|
||||||
Return =>
|
Return =>
|
||||||
|
|
|
@ -109,6 +109,13 @@ pub trait Visitor<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Terminator::SwitchInt { ref discr, switch_ty: _, values: _, ref targets } => {
|
||||||
|
self.visit_lvalue(discr, LvalueContext::Inspect);
|
||||||
|
for &target in targets {
|
||||||
|
self.visit_branch(block, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Terminator::Diverge |
|
Terminator::Diverge |
|
||||||
Terminator::Return => {
|
Terminator::Return => {
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mir::Terminator::SwitchInt { .. } => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
mir::Terminator::Diverge => {
|
mir::Terminator::Diverge => {
|
||||||
if let Some(llpersonalityslot) = self.llpersonalityslot {
|
if let Some(llpersonalityslot) = self.llpersonalityslot {
|
||||||
let lp = build::Load(bcx, llpersonalityslot);
|
let lp = build::Load(bcx, llpersonalityslot);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue