Skip to content

Commit

Permalink
Auto merge of #123088 - tgross35:f16-f128-pattern-analysis, r=Nadrieril
Browse files Browse the repository at this point in the history
Replace `f16` and `f128` pattern matching stubs with real implementations

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.

r? `@Nadrieril`
  • Loading branch information
bors committed Jun 23, 2024
2 parents c3d7fb3 + 28ce7cd commit aabbf84
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 43 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
5 changes: 0 additions & 5 deletions tests/crashes/122587-1.rs

This file was deleted.

14 changes: 14 additions & 0 deletions tests/ui/consts/const_in_pattern/f16-f128-const-reassign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ check-pass
// issue: rust-lang/rust#122587

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

const h: f16 = 0.0f16;
const q: f128 = 0.0f128;

pub fn main() {
let h = 0.0f16 else { unreachable!() };
let q = 0.0f128 else { unreachable!() };
}
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
Loading

0 comments on commit aabbf84

Please sign in to comment.