blob: 3473155a3ea3ed8c82a4b0861499b6e3e2aff182 [file] [log] [blame]
//! Simplifying Candidates
//!
//! *Simplifying* a match pair `place @ pattern` means breaking it down
//! into bindings or other, simpler match pairs. For example:
//!
//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
//! - `place @ x` can be simplified to `[]` by binding `x` to `place`
//!
//! The `simplify_candidate` routine just repeatedly applies these
//! sort of simplifications until there is nothing left to
//! simplify. Match pairs cannot be simplified if they require some
//! sort of test: for example, testing which variant an enum is, or
//! testing a value against a constant.
use crate::build::Builder;
use crate::build::matches::{Ascription, Binding, MatchPair, Candidate};
use crate::hair::{self, *};
use rustc::ty;
use rustc::ty::layout::{Integer, IntegerExt, Size};
use syntax::attr::{SignedInt, UnsignedInt};
use rustc::hir::RangeEnd;
use rustc::mir::interpret::truncate;
use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> {
pub fn simplify_candidate<'pat>(&mut self,
candidate: &mut Candidate<'pat, 'tcx>) {
// repeatedly simplify match pairs until fixed point is reached
loop {
let match_pairs = mem::take(&mut candidate.match_pairs);
let mut changed = false;
for match_pair in match_pairs {
match self.simplify_match_pair(match_pair, candidate) {
Ok(()) => {
changed = true;
}
Err(match_pair) => {
candidate.match_pairs.push(match_pair);
}
}
}
if !changed {
return; // if we were not able to simplify any, done.
}
}
}
/// Tries to simplify `match_pair`, returning `Ok(())` if
/// successful. If successful, new match pairs and bindings will
/// have been pushed into the candidate. If no simplification is
/// possible, `Err` is returned and no changes are made to
/// candidate.
fn simplify_match_pair<'pat>(&mut self,
match_pair: MatchPair<'pat, 'tcx>,
candidate: &mut Candidate<'pat, 'tcx>)
-> Result<(), MatchPair<'pat, 'tcx>> {
let tcx = self.hir.tcx();
match *match_pair.pattern.kind {
PatternKind::AscribeUserType {
ref subpattern,
ascription: hair::pattern::Ascription {
variance,
ref user_ty,
user_ty_span,
},
} => {
// Apply the type ascription to the value at `match_pair.place`, which is the
// value being matched, taking the variance field into account.
candidate.ascriptions.push(Ascription {
span: user_ty_span,
user_ty: user_ty.clone(),
source: match_pair.place.clone(),
variance,
});
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
Ok(())
}
PatternKind::Wild => {
// nothing left to do
Ok(())
}
PatternKind::Binding { name, mutability, mode, var, ty, ref subpattern } => {
candidate.bindings.push(Binding {
name,
mutability,
span: match_pair.pattern.span,
source: match_pair.place.clone(),
var_id: var,
var_ty: ty,
binding_mode: mode,
});
if let Some(subpattern) = subpattern.as_ref() {
// this is the `x @ P` case; have to keep matching against `P` now
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
}
Ok(())
}
PatternKind::Constant { .. } => {
// FIXME normalize patterns when possible
Err(match_pair)
}
PatternKind::Range(PatternRange { lo, hi, end }) => {
let (range, bias) = match lo.ty.sty {
ty::Char => {
(Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
}
ty::Int(ity) => {
let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
let max = truncate(u128::max_value(), size);
let bias = 1u128 << (size.bits() - 1);
(Some((0, max, size)), bias)
}
ty::Uint(uty) => {
let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size();
let max = truncate(u128::max_value(), size);
(Some((0, max, size)), 0)
}
_ => (None, 0),
};
if let Some((min, max, sz)) = range {
if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) {
// We want to compare ranges numerically, but the order of the bitwise
// representation of signed integers does not match their numeric order.
// Thus, to correct the ordering, we need to shift the range of signed
// integers to correct the comparison. This is achieved by XORing with a
// bias (see pattern/_match.rs for another pertinent example of this
// pattern).
let (lo, hi) = (lo ^ bias, hi ^ bias);
if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
// Irrefutable pattern match.
return Ok(());
}
}
}
Err(match_pair)
}
PatternKind::Slice { ref prefix, ref slice, ref suffix } => {
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
// irrefutable
self.prefix_slice_suffix(&mut candidate.match_pairs,
&match_pair.place,
prefix,
slice.as_ref(),
suffix);
Ok(())
} else {
Err(match_pair)
}
}
PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => {
let irrefutable = adt_def.variants.iter_enumerated().all(|(i, v)| {
i == variant_index || {
self.hir.tcx().features().exhaustive_patterns &&
!v.uninhabited_from(self.hir.tcx(), substs, adt_def.adt_kind()).is_empty()
}
});
if irrefutable {
let place = match_pair.place.downcast(adt_def, variant_index);
candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns));
Ok(())
} else {
Err(match_pair)
}
}
PatternKind::Array { ref prefix, ref slice, ref suffix } => {
self.prefix_slice_suffix(&mut candidate.match_pairs,
&match_pair.place,
prefix,
slice.as_ref(),
suffix);
Ok(())
}
PatternKind::Leaf { ref subpatterns } => {
// tuple struct, match subpats (if any)
candidate.match_pairs
.extend(self.field_match_pairs(match_pair.place, subpatterns));
Ok(())
}
PatternKind::Deref { ref subpattern } => {
let place = match_pair.place.deref();
candidate.match_pairs.push(MatchPair::new(place, subpattern));
Ok(())
}
}
}
}