Skip to content

Commit

Permalink
Replace f16 and f128 pattern matching stubs with real implementat…
Browse files Browse the repository at this point in the history
…ions

This section of code depends on `rustc_apfloat` rather than our internal
types, so this is one potential ICE that we should be able to melt now.

This also fixes some missing range and match handling in `rustc_middle`.
  • Loading branch information
tgross35 committed Jun 23, 2024
1 parent acb6273 commit 753f162
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 38 deletions.
12 changes: 12 additions & 0 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,12 @@ impl<'tcx> PatRangeBoundary<'tcx> {
let b = other.eval_bits(ty, tcx, param_env);

match ty.kind() {
ty::Float(ty::FloatTy::F16) => {
use rustc_apfloat::Float;
let a = rustc_apfloat::ieee::Half::from_bits(a);
let b = rustc_apfloat::ieee::Half::from_bits(b);
a.partial_cmp(&b)
}
ty::Float(ty::FloatTy::F32) => {
use rustc_apfloat::Float;
let a = rustc_apfloat::ieee::Single::from_bits(a);
Expand All @@ -1059,6 +1065,12 @@ impl<'tcx> PatRangeBoundary<'tcx> {
let b = rustc_apfloat::ieee::Double::from_bits(b);
a.partial_cmp(&b)
}
ty::Float(ty::FloatTy::F128) => {
use rustc_apfloat::Float;
let a = rustc_apfloat::ieee::Quad::from_bits(a);
let b = rustc_apfloat::ieee::Quad::from_bits(b);
a.partial_cmp(&b)
}
ty::Int(ity) => {
let size = rustc_target::abi::Integer::from_int_ty(&tcx, *ity).size();
let a = size.sign_extend(a) as i128;
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,7 @@ impl<'tcx> Ty<'tcx> {
/// Returns the minimum and maximum values for the given numeric type (including `char`s) or
/// returns `None` if the type is not numeric.
pub fn numeric_min_and_max_as_bits(self, tcx: TyCtxt<'tcx>) -> Option<(u128, u128)> {
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
Some(match self.kind() {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = self.int_size_and_signed(tcx);
Expand All @@ -1206,12 +1206,14 @@ impl<'tcx> Ty<'tcx> {
(min, max)
}
ty::Char => (0, std::char::MAX as u128),
ty::Float(ty::FloatTy::F16) => ((-Half::INFINITY).to_bits(), Half::INFINITY.to_bits()),
ty::Float(ty::FloatTy::F32) => {
((-Single::INFINITY).to_bits(), Single::INFINITY.to_bits())
}
ty::Float(ty::FloatTy::F64) => {
((-Double::INFINITY).to_bits(), Double::INFINITY.to_bits())
}
ty::Float(ty::FloatTy::F128) => ((-Quad::INFINITY).to_bits(), Quad::INFINITY.to_bits()),
_ => return None,
})
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,10 @@ impl<'tcx> ConstToPat<'tcx> {
ty::Float(flt) => {
let v = cv.unwrap_leaf();
let is_nan = match flt {
ty::FloatTy::F16 => unimplemented!("f16_f128"),
ty::FloatTy::F16 => v.to_f16().is_nan(),
ty::FloatTy::F32 => v.to_f32().is_nan(),
ty::FloatTy::F64 => v.to_f64().is_nan(),
ty::FloatTy::F128 => unimplemented!("f16_f128"),
ty::FloatTy::F128 => v.to_f128().is_nan(),
};
if is_nan {
// NaNs are not ever equal to anything so they make no sense as patterns.
Expand Down
27 changes: 26 additions & 1 deletion compiler/rustc_pattern_analysis/src/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ use std::iter::once;

use smallvec::SmallVec;

use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
use rustc_index::bit_set::{BitSet, GrowableBitSet};
use rustc_index::IndexVec;

Expand Down Expand Up @@ -692,8 +692,10 @@ pub enum Constructor<Cx: PatCx> {
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
IntRange(IntRange),
/// Ranges of floating-point literal values (`2.0..=5.2`).
F16Range(IeeeFloat<HalfS>, IeeeFloat<HalfS>, RangeEnd),
F32Range(IeeeFloat<SingleS>, IeeeFloat<SingleS>, RangeEnd),
F64Range(IeeeFloat<DoubleS>, IeeeFloat<DoubleS>, RangeEnd),
F128Range(IeeeFloat<QuadS>, IeeeFloat<QuadS>, RangeEnd),
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
Str(Cx::StrLit),
/// Constants that must not be matched structurally. They are treated as black boxes for the
Expand Down Expand Up @@ -735,8 +737,10 @@ impl<Cx: PatCx> Clone for Constructor<Cx> {
Constructor::UnionField => Constructor::UnionField,
Constructor::Bool(b) => Constructor::Bool(*b),
Constructor::IntRange(range) => Constructor::IntRange(*range),
Constructor::F16Range(lo, hi, end) => Constructor::F16Range(lo.clone(), *hi, *end),
Constructor::F32Range(lo, hi, end) => Constructor::F32Range(lo.clone(), *hi, *end),
Constructor::F64Range(lo, hi, end) => Constructor::F64Range(lo.clone(), *hi, *end),
Constructor::F128Range(lo, hi, end) => Constructor::F128Range(lo.clone(), *hi, *end),
Constructor::Str(value) => Constructor::Str(value.clone()),
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
Constructor::Or => Constructor::Or,
Expand Down Expand Up @@ -812,6 +816,14 @@ impl<Cx: PatCx> Constructor<Cx> {
(Bool(self_b), Bool(other_b)) => self_b == other_b,

(IntRange(self_range), IntRange(other_range)) => self_range.is_subrange(other_range),
(F16Range(self_from, self_to, self_end), F16Range(other_from, other_to, other_end)) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Some(Ordering::Less) => true,
Some(Ordering::Equal) => other_end == self_end,
_ => false,
}
}
(F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Expand All @@ -828,6 +840,17 @@ impl<Cx: PatCx> Constructor<Cx> {
_ => false,
}
}
(
F128Range(self_from, self_to, self_end),
F128Range(other_from, other_to, other_end),
) => {
self_from.ge(other_from)
&& match self_to.partial_cmp(other_to) {
Some(Ordering::Less) => true,
Some(Ordering::Equal) => other_end == self_end,
_ => false,
}
}
(Str(self_val), Str(other_val)) => {
// FIXME Once valtrees are available we can directly use the bytes
// in the `Str` variant of the valtree for the comparison here.
Expand Down Expand Up @@ -906,8 +929,10 @@ impl<Cx: PatCx> Constructor<Cx> {
Bool(b) => write!(f, "{b}")?,
// Best-effort, will render signed ranges incorrectly
IntRange(range) => write!(f, "{range:?}")?,
F16Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
F128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
Str(value) => write!(f, "{value:?}")?,
Opaque(..) => write!(f, "<constant pattern>")?,
Or => {
Expand Down
50 changes: 43 additions & 7 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
},
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
| Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
| PrivateUninhabited | Wildcard => &[],
Or => {
bug!("called `Fields::wildcards` on an `Or` ctor")
}
Expand All @@ -275,8 +276,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
},
Ref => 1,
Slice(slice) => slice.arity(),
Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
| Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
| PrivateUninhabited | Wildcard => 0,
Or => bug!("The `Or` constructor doesn't have a fixed arity"),
}
}
Expand Down Expand Up @@ -546,6 +548,18 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F16) => {
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
Some(bits) => {
use rustc_apfloat::Float;
let value = rustc_apfloat::ieee::Half::from_bits(bits);
F16Range(value, value, RangeEnd::Included)
}
None => Opaque(OpaqueId::new()),
};
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F32) => {
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
Some(bits) => {
Expand All @@ -570,6 +584,18 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
fields = vec![];
arity = 0;
}
ty::Float(ty::FloatTy::F128) => {
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
Some(bits) => {
use rustc_apfloat::Float;
let value = rustc_apfloat::ieee::Quad::from_bits(bits);
F128Range(value, value, RangeEnd::Included)
}
None => Opaque(OpaqueId::new()),
};
fields = vec![];
arity = 0;
}
ty::Ref(_, t, _) if t.is_str() => {
// We want a `&str` constant to behave like a `Deref` pattern, to be compatible
// with other `Deref` patterns. This could have been done in `const_to_pat`,
Expand Down Expand Up @@ -611,7 +637,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.param_env));
let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.param_env));
match fty {
ty::FloatTy::F16 => unimplemented!("f16_f128"),
ty::FloatTy::F16 => {
use rustc_apfloat::ieee::Half;
let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY);
let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY);
F16Range(lo, hi, end)
}
ty::FloatTy::F32 => {
use rustc_apfloat::ieee::Single;
let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
Expand All @@ -624,7 +655,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
F64Range(lo, hi, end)
}
ty::FloatTy::F128 => unimplemented!("f16_f128"),
ty::FloatTy::F128 => {
use rustc_apfloat::ieee::Quad;
let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY);
let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY);
F128Range(lo, hi, end)
}
}
}
_ => bug!("invalid type for range pattern: {}", ty.inner()),
Expand Down Expand Up @@ -837,7 +873,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
),
F32Range(..) | F64Range(..) | Opaque(..) | Or => {
F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
bug!("can't convert to pattern: {:?}", pat)
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// via `.contains(...)` and make sure the dynamic semantics match.

#![allow(unreachable_patterns)]
#![feature(f128)]
#![feature(f16)]

macro_rules! yes {
($scrutinee:expr, $($t:tt)+) => {
Expand Down Expand Up @@ -39,6 +41,17 @@ fn range_to_inclusive() {
assert!(yes!('a', ..='a'));
assert!(!yes!('b', ..='a'));

// f16; `..=X`
// FIXME(f16_f128): remove gate when ABI issues are resolved
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
{
// FIXME(f16_f128): enable infinity tests when constants are available
// assert!(yes!(f16::NEG_INFINITY, ..=f16::NEG_INFINITY));
// assert!(yes!(f16::NEG_INFINITY, ..=1.0f16));
assert!(yes!(1.5f16, ..=1.5f16));
assert!(!yes!(1.6f16, ..=-1.5f16));
}

// f32; `..=X`
assert!(yes!(f32::NEG_INFINITY, ..=f32::NEG_INFINITY));
assert!(yes!(f32::NEG_INFINITY, ..=1.0f32));
Expand All @@ -50,6 +63,17 @@ fn range_to_inclusive() {
assert!(yes!(f64::NEG_INFINITY, ..=1.0f64));
assert!(yes!(1.5f64, ..=1.5f64));
assert!(!yes!(1.6f64, ..=-1.5f64));

// f128; `..=X`
// FIXME(f16_f128): remove gate when ABI issues are resolved
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
{
// FIXME(f16_f128): enable infinity tests when constants are available
// assert!(yes!(f128::NEG_INFINITY, ..=f128::NEG_INFINITY));
// assert!(yes!(f128::NEG_INFINITY, ..=1.0f128));
assert!(yes!(1.5f128, ..=1.5f128));
assert!(!yes!(1.6f128, ..=-1.5f128));
}
}

fn range_to() {
Expand Down Expand Up @@ -83,6 +107,18 @@ fn range_to() {
assert!(!yes!('a', ..'a'));
assert!(!yes!('b', ..'a'));

// f16; `..X`
// FIXME(f16_f128): remove gate when ABI issues are resolved
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
{
// FIXME(f16_f128): enable infinity tests when constants are available
// assert!(yes!(f16::NEG_INFINITY, ..1.0f16));
assert!(!yes!(1.5f16, ..1.5f16));
const E16: f16 = 1.5f16 + f16::EPSILON;
assert!(yes!(1.5f16, ..E16));
assert!(!yes!(1.6f16, ..1.5f16));
}

// f32; `..X`
assert!(yes!(f32::NEG_INFINITY, ..1.0f32));
assert!(!yes!(1.5f32, ..1.5f32));
Expand All @@ -96,6 +132,18 @@ fn range_to() {
const E64: f64 = 1.5f64 + f64::EPSILON;
assert!(yes!(1.5f64, ..E64));
assert!(!yes!(1.6f64, ..1.5f64));

// f128; `..X`
// FIXME(f16_f128): remove gate when ABI issues are resolved
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
{
// FIXME(f16_f128): enable infinity tests when constants are available
// assert!(yes!(f128::NEG_INFINITY, ..1.0f128));
assert!(!yes!(1.5f128, ..1.5f128));
const E128: f128 = 1.5f128 + f128::EPSILON;
assert!(yes!(1.5f128, ..E128));
assert!(!yes!(1.6f128, ..1.5f128));
}
}

fn range_from() {
Expand Down Expand Up @@ -129,6 +177,21 @@ fn range_from() {
assert!(!yes!('a', 'b'..));
assert!(yes!(core::char::MAX, core::char::MAX..));

// f16; `X..`
// FIXME(f16_f128): remove gate when ABI issues are resolved
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
{
// FIXME(f16_f128): enable infinity tests when constants are available
// assert!(yes!(f16::NEG_INFINITY, f16::NEG_INFINITY..));
// assert!(yes!(f16::INFINITY, f16::NEG_INFINITY..));
// assert!(!yes!(f16::NEG_INFINITY, 1.0f16..));
// assert!(yes!(f16::INFINITY, 1.0f16..));
assert!(!yes!(1.0f16 - f16::EPSILON, 1.0f16..));
assert!(yes!(1.0f16, 1.0f16..));
// assert!(yes!(f16::INFINITY, 1.0f16..));
// assert!(yes!(f16::INFINITY, f16::INFINITY..));
}

// f32; `X..`
assert!(yes!(f32::NEG_INFINITY, f32::NEG_INFINITY..));
assert!(yes!(f32::INFINITY, f32::NEG_INFINITY..));
Expand All @@ -148,6 +211,21 @@ fn range_from() {
assert!(yes!(1.0f64, 1.0f64..));
assert!(yes!(f64::INFINITY, 1.0f64..));
assert!(yes!(f64::INFINITY, f64::INFINITY..));

// f128; `X..`
// FIXME(f16_f128): remove gate when ABI issues are resolved
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
{
// FIXME(f16_f128): enable infinity tests when constants are available
// assert!(yes!(f128::NEG_INFINITY, f128::NEG_INFINITY..));
// assert!(yes!(f128::INFINITY, f128::NEG_INFINITY..));
// assert!(!yes!(f128::NEG_INFINITY, 1.0f128..));
// assert!(yes!(f128::INFINITY, 1.0f128..));
assert!(!yes!(1.0f128 - f128::EPSILON, 1.0f128..));
assert!(yes!(1.0f128, 1.0f128..));
// assert!(yes!(f128::INFINITY, 1.0f128..));
// assert!(yes!(f128::INFINITY, f128::INFINITY..));
}
}

fn main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn main() {
m!(0, ..i128::MIN);
//~^ ERROR lower range bound must be less than upper

// FIXME(f16_f128): add tests when NEG_INFINITY is available
m!(0f32, ..f32::NEG_INFINITY);
//~^ ERROR lower range bound must be less than upper
m!(0f64, ..f64::NEG_INFINITY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,19 @@ LL | m!(0, ..i128::MIN);
| ^^^^^^^^^^^

error[E0579]: lower range bound must be less than upper
--> $DIR/half-open-range-pats-thir-lower-empty.rs:30:14
--> $DIR/half-open-range-pats-thir-lower-empty.rs:31:14
|
LL | m!(0f32, ..f32::NEG_INFINITY);
| ^^^^^^^^^^^^^^^^^^^

error[E0579]: lower range bound must be less than upper
--> $DIR/half-open-range-pats-thir-lower-empty.rs:32:14
--> $DIR/half-open-range-pats-thir-lower-empty.rs:33:14
|
LL | m!(0f64, ..f64::NEG_INFINITY);
| ^^^^^^^^^^^^^^^^^^^

error[E0579]: lower range bound must be less than upper
--> $DIR/half-open-range-pats-thir-lower-empty.rs:35:13
--> $DIR/half-open-range-pats-thir-lower-empty.rs:36:13
|
LL | m!('a', ..'\u{0}');
| ^^^^^^^^^
Expand Down
Loading

0 comments on commit 753f162

Please sign in to comment.