Handle ranges of float consistently

This deconfuses the comparison of floats, that currently mixed ranges
and non-ranges.
This commit is contained in:
Nadrieril 2020-10-17 23:12:28 +01:00
parent f504e9a425
commit aa4172076e

View File

@ -834,13 +834,6 @@ enum Constructor<'tcx> {
}
impl<'tcx> Constructor<'tcx> {
fn is_slice(&self) -> bool {
match self {
Slice(_) => true,
_ => false,
}
}
fn variant_index_for_adt<'a>(
&self,
cx: &MatchCheckCtxt<'a, 'tcx>,
@ -2111,7 +2104,10 @@ fn pat_constructor<'tcx>(
if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
Some(IntRange(int_range))
} else {
Some(ConstantValue(value))
match value.ty.kind() {
ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)),
_ => Some(ConstantValue(value)),
}
}
}
PatKind::Range(PatRange { lo, hi, end }) => {
@ -2443,35 +2439,6 @@ fn lint_overlapping_patterns<'tcx>(
}
}
fn constructor_covered_by_range<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ctor: &Constructor<'tcx>,
pat: &Pat<'tcx>,
) -> Option<()> {
if let Single = ctor {
return Some(());
}
let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
_ => bug!("`constructor_covered_by_range` called with {:?}", pat),
};
let (ctor_from, ctor_to, ctor_end) = match *ctor {
ConstantValue(value) => (value, value, RangeEnd::Included),
FloatRange(from, to, ctor_end) => (from, to, ctor_end),
_ => bug!("`constructor_covered_by_range` called with {:?}", ctor),
};
trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty);
let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?;
let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?;
let intersects = (from == Ordering::Greater || from == Ordering::Equal)
&& (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal));
if intersects { Some(()) } else { None }
}
/// This is the main specialization step. It expands the pattern
/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
@ -2516,27 +2483,51 @@ fn specialize_one_pattern<'p, 'tcx>(
PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)),
PatKind::Constant { value } if constructor.is_slice() => {
span_bug!(pat.span, "unexpected const-val {:?} with ctor {:?}", value, constructor)
}
PatKind::Constant { .. } | PatKind::Range { .. } => {
// If the constructor is a:
// - Single value: add a row if the pattern contains the constructor.
// - Range: add a row if the constructor intersects the pattern.
if let IntRange(ctor) = constructor {
let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
ctor.intersection(cx.tcx, &pat)?;
// Constructor splitting should ensure that all intersections we encounter
// are actually inclusions.
assert!(ctor.is_subrange(&pat));
} else {
// Fallback for non-ranges and ranges that involve
// floating-point numbers, which are not conveniently handled
// by `IntRange`. For these cases, the constructor may not be a
// range so intersection actually devolves into being covered
// by the pattern.
constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?;
match constructor {
Single => {}
IntRange(ctor) => {
let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
ctor.intersection(cx.tcx, &pat)?;
// Constructor splitting should ensure that all intersections we encounter
// are actually inclusions.
assert!(ctor.is_subrange(&pat));
}
FloatRange(ctor_from, ctor_to, ctor_end) => {
let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
_ => unreachable!(), // This is ensured by the branch we're in
};
let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?;
let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?;
let intersects = (from == Ordering::Greater || from == Ordering::Equal)
&& (to == Ordering::Less
|| (pat_end == *ctor_end && to == Ordering::Equal));
if !intersects {
return None;
}
}
ConstantValue(ctor_value) => {
let pat_value = match *pat.kind {
PatKind::Constant { value } => value,
_ => span_bug!(
pat.span,
"unexpected range pattern {:?} for constant value ctor",
pat
),
};
// FIXME: there's probably a more direct way of comparing for equality
if compare_const_vals(cx.tcx, ctor_value, pat_value, cx.param_env, pat.ty)?
!= Ordering::Equal
{
return None;
}
}
_ => {
span_bug!(pat.span, "unexpected pattern {:?} with ctor {:?}", pat, constructor)
}
}
Some(Fields::empty())
}