use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
use crate::build::Builder;
use crate::thir::{self, *};
use rustc_attr::{SignedInt, UnsignedInt};
use rustc_hir::RangeEnd;
use rustc_middle::mir::Place;
use rustc_middle::ty;
use rustc_middle::ty::layout::IntegerExt;
use rustc_target::abi::{Integer, Size};
use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(super) fn simplify_candidate<'pat>(
&mut self,
candidate: &mut Candidate<'pat, 'tcx>,
) -> bool {
debug!(?candidate, "simplify_candidate");
let mut existing_bindings = mem::take(&mut candidate.bindings);
let mut new_bindings = Vec::new();
loop {
let match_pairs = mem::take(&mut candidate.match_pairs);
if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] =
*match_pairs
{
existing_bindings.extend_from_slice(&new_bindings);
mem::swap(&mut candidate.bindings, &mut existing_bindings);
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
return true;
}
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);
}
}
}
candidate.bindings.extend_from_slice(&new_bindings);
mem::swap(&mut candidate.bindings, &mut new_bindings);
candidate.bindings.clear();
if !changed {
existing_bindings.extend_from_slice(&new_bindings);
mem::swap(&mut candidate.bindings, &mut existing_bindings);
candidate
.match_pairs
.sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
debug!(simplified = ?candidate, "simplify_candidate");
return false;
}
}
}
fn create_or_subcandidates<'pat>(
&mut self,
candidate: &Candidate<'pat, 'tcx>,
place: Place<'tcx>,
pats: &'pat [Pat<'tcx>],
) -> Vec<Candidate<'pat, 'tcx>> {
pats.iter()
.map(|pat| {
let mut candidate = Candidate::new(place, pat, candidate.has_guard);
self.simplify_candidate(&mut candidate);
candidate
})
.collect()
}
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 {
PatKind::AscribeUserType {
ref subpattern,
ascription: thir::pattern::Ascription { variance, user_ty, user_ty_span },
} => {
candidate.ascriptions.push(Ascription {
span: user_ty_span,
user_ty,
source: match_pair.place,
variance,
});
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
Ok(())
}
PatKind::Wild => {
Ok(())
}
PatKind::Binding { name, mutability, mode, var, ty, ref subpattern, is_primary: _ } => {
candidate.bindings.push(Binding {
name,
mutability,
span: match_pair.pattern.span,
source: match_pair.place,
var_id: var,
var_ty: ty,
binding_mode: mode,
});
if let Some(subpattern) = subpattern.as_ref() {
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern));
}
Ok(())
}
PatKind::Constant { .. } => {
Err(match_pair)
}
PatKind::Range(PatRange { lo, hi, end }) => {
let (range, bias) = match *lo.ty.kind() {
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 = size.truncate(u128::MAX);
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 = size.truncate(u128::MAX);
(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)) {
let (lo, hi) = (lo ^ bias, hi ^ bias);
if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
return Ok(());
}
}
}
Err(match_pair)
}
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
self.prefix_slice_suffix(
&mut candidate.match_pairs,
&match_pair.place,
prefix,
slice.as_ref(),
suffix,
);
Ok(())
} else {
Err(match_pair)
}
}
PatKind::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(),
self.hir.param_env,
)
.is_empty()
}
}) && (adt_def.did.is_local()
|| !adt_def.is_variant_list_non_exhaustive());
if irrefutable {
let place = tcx.mk_place_downcast(match_pair.place, adt_def, variant_index);
candidate.match_pairs.extend(self.field_match_pairs(place, subpatterns));
Ok(())
} else {
Err(match_pair)
}
}
PatKind::Array { ref prefix, ref slice, ref suffix } => {
self.prefix_slice_suffix(
&mut candidate.match_pairs,
&match_pair.place,
prefix,
slice.as_ref(),
suffix,
);
Ok(())
}
PatKind::Leaf { ref subpatterns } => {
candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
Ok(())
}
PatKind::Deref { ref subpattern } => {
let place = tcx.mk_place_deref(match_pair.place);
candidate.match_pairs.push(MatchPair::new(place, subpattern));
Ok(())
}
PatKind::Or { .. } => Err(match_pair),
}
}
}