diff --git a/.reuse/dep5 b/.reuse/dep5 index 9a59f455fe9a..5546a7cf3911 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -38,11 +38,6 @@ Files: compiler/* Copyright: The Rust Project Developers (see https://thanks.rust-lang.org) License: MIT or Apache-2.0 -Files: compiler/rustc_apfloat/* -Copyright: LLVM APFloat authors - The Rust Project Developers (see https://thanks.rust-lang.org) -License: NCSA AND (MIT OR Apache-2.0) - Files: compiler/rustc_codegen_cranelift/src/cranelift_native.rs Copyright: The Cranelift Project Developers The Rust Project Developers (see https://thanks.rust-lang.org) diff --git a/Cargo.lock b/Cargo.lock index 72f0236fd08a..cc5c163b8bea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3135,7 +3135,9 @@ dependencies = [ [[package]] name = "rustc_apfloat" -version = "0.0.0" +version = "0.2.0+llvm-462a31f5a5ab" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465187772033a5ee566f69fe008df03628fce549a0899aae76f0a0c2e34696be" dependencies = [ "bitflags 1.3.2", "smallvec", @@ -4725,9 +4727,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "snap" diff --git a/LICENSES/NCSA.txt b/LICENSES/NCSA.txt deleted file mode 100644 index cf5413effa25..000000000000 --- a/LICENSES/NCSA.txt +++ /dev/null @@ -1,15 +0,0 @@ -University of Illinois/NCSA Open Source License - -Copyright (c) . All rights reserved. - -Developed by: - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. - - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. - - * Neither the names of , nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/compiler/rustc_apfloat/Cargo.toml b/compiler/rustc_apfloat/Cargo.toml deleted file mode 100644 index 98305201bc94..000000000000 --- a/compiler/rustc_apfloat/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "rustc_apfloat" -version = "0.0.0" -edition = "2021" - -[dependencies] -bitflags = "1.2.1" -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs deleted file mode 100644 index 2286712f0256..000000000000 --- a/compiler/rustc_apfloat/src/ieee.rs +++ /dev/null @@ -1,2757 +0,0 @@ -use crate::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO}; -use crate::{Float, FloatConvert, ParseError, Round, Status, StatusAnd}; - -use core::cmp::{self, Ordering}; -use core::fmt::{self, Write}; -use core::marker::PhantomData; -use core::mem; -use core::ops::Neg; -use smallvec::{smallvec, SmallVec}; - -#[must_use] -pub struct IeeeFloat { - /// Absolute significand value (including the integer bit). - sig: [Limb; 1], - - /// The signed unbiased exponent of the value. - exp: ExpInt, - - /// What kind of floating point number this is. - category: Category, - - /// Sign bit of the number. - sign: bool, - - marker: PhantomData, -} - -/// Fundamental unit of big integer arithmetic, but also -/// large to store the largest significands by itself. -type Limb = u128; -const LIMB_BITS: usize = 128; -fn limbs_for_bits(bits: usize) -> usize { - (bits + LIMB_BITS - 1) / LIMB_BITS -} - -/// Enum that represents what fraction of the LSB truncated bits of an fp number -/// represent. -/// -/// This essentially combines the roles of guard and sticky bits. -#[must_use] -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum Loss { - // Example of truncated bits: - ExactlyZero, // 000000 - LessThanHalf, // 0xxxxx x's not all zero - ExactlyHalf, // 100000 - MoreThanHalf, // 1xxxxx x's not all zero -} - -/// Represents floating point arithmetic semantics. -pub trait Semantics: Sized { - /// Total number of bits in the in-memory format. - const BITS: usize; - - /// Number of bits in the significand. This includes the integer bit. - const PRECISION: usize; - - /// The largest E such that 2E is representable; this matches the - /// definition of IEEE 754. - const MAX_EXP: ExpInt; - - /// The smallest E such that 2E is a normalized number; this - /// matches the definition of IEEE 754. - const MIN_EXP: ExpInt = -Self::MAX_EXP + 1; - - /// The significand bit that marks NaN as quiet. - const QNAN_BIT: usize = Self::PRECISION - 2; - - /// The significand bitpattern to mark a NaN as quiet. - /// NOTE: for X87DoubleExtended we need to set two bits instead of 2. - const QNAN_SIGNIFICAND: Limb = 1 << Self::QNAN_BIT; - - fn from_bits(bits: u128) -> IeeeFloat { - assert!(Self::BITS > Self::PRECISION); - - let sign = bits & (1 << (Self::BITS - 1)); - let exponent = (bits & !sign) >> (Self::PRECISION - 1); - let mut r = IeeeFloat { - sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)], - // Convert the exponent from its bias representation to a signed integer. - exp: (exponent as ExpInt) - Self::MAX_EXP, - category: Category::Zero, - sign: sign != 0, - marker: PhantomData, - }; - - if r.exp == Self::MIN_EXP - 1 && r.sig == [0] { - // Exponent, significand meaningless. - r.category = Category::Zero; - } else if r.exp == Self::MAX_EXP + 1 && r.sig == [0] { - // Exponent, significand meaningless. - r.category = Category::Infinity; - } else if r.exp == Self::MAX_EXP + 1 && r.sig != [0] { - // Sign, exponent, significand meaningless. - r.category = Category::NaN; - } else { - r.category = Category::Normal; - if r.exp == Self::MIN_EXP - 1 { - // Denormal. - r.exp = Self::MIN_EXP; - } else { - // Set integer bit. - sig::set_bit(&mut r.sig, Self::PRECISION - 1); - } - } - - r - } - - fn to_bits(x: IeeeFloat) -> u128 { - assert!(Self::BITS > Self::PRECISION); - - // Split integer bit from significand. - let integer_bit = sig::get_bit(&x.sig, Self::PRECISION - 1); - let mut significand = x.sig[0] & ((1 << (Self::PRECISION - 1)) - 1); - let exponent = match x.category { - Category::Normal => { - if x.exp == Self::MIN_EXP && !integer_bit { - // Denormal. - Self::MIN_EXP - 1 - } else { - x.exp - } - } - Category::Zero => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 0; - Self::MIN_EXP - 1 - } - Category::Infinity => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 0; - Self::MAX_EXP + 1 - } - Category::NaN => Self::MAX_EXP + 1, - }; - - // Convert the exponent from a signed integer to its bias representation. - let exponent = (exponent + Self::MAX_EXP) as u128; - - ((x.sign as u128) << (Self::BITS - 1)) | (exponent << (Self::PRECISION - 1)) | significand - } -} - -impl Copy for IeeeFloat {} -impl Clone for IeeeFloat { - fn clone(&self) -> Self { - *self - } -} - -macro_rules! ieee_semantics { - ($($name:ident = $sem:ident($bits:tt : $exp_bits:tt)),*) => { - $(pub struct $sem;)* - $(pub type $name = IeeeFloat<$sem>;)* - $(impl Semantics for $sem { - const BITS: usize = $bits; - const PRECISION: usize = ($bits - 1 - $exp_bits) + 1; - const MAX_EXP: ExpInt = (1 << ($exp_bits - 1)) - 1; - })* - } -} - -ieee_semantics! { - Half = HalfS(16:5), - Single = SingleS(32:8), - Double = DoubleS(64:11), - Quad = QuadS(128:15) -} - -pub struct X87DoubleExtendedS; -pub type X87DoubleExtended = IeeeFloat; -impl Semantics for X87DoubleExtendedS { - const BITS: usize = 80; - const PRECISION: usize = 64; - const MAX_EXP: ExpInt = (1 << (15 - 1)) - 1; - - /// For x87 extended precision, we want to make a NaN, not a - /// pseudo-NaN. Maybe we should expose the ability to make - /// pseudo-NaNs? - const QNAN_SIGNIFICAND: Limb = 0b11 << Self::QNAN_BIT; - - /// Integer bit is explicit in this format. Intel hardware (387 and later) - /// does not support these bit patterns: - /// exponent = all 1's, integer bit 0, significand 0 ("pseudoinfinity") - /// exponent = all 1's, integer bit 0, significand nonzero ("pseudoNaN") - /// exponent = 0, integer bit 1 ("pseudodenormal") - /// exponent != 0 nor all 1's, integer bit 0 ("unnormal") - /// At the moment, the first two are treated as NaNs, the second two as Normal. - fn from_bits(bits: u128) -> IeeeFloat { - let sign = bits & (1 << (Self::BITS - 1)); - let exponent = (bits & !sign) >> Self::PRECISION; - let mut r = IeeeFloat { - sig: [bits & ((1 << (Self::PRECISION - 1)) - 1)], - // Convert the exponent from its bias representation to a signed integer. - exp: (exponent as ExpInt) - Self::MAX_EXP, - category: Category::Zero, - sign: sign != 0, - marker: PhantomData, - }; - - if r.exp == Self::MIN_EXP - 1 && r.sig == [0] { - // Exponent, significand meaningless. - r.category = Category::Zero; - } else if r.exp == Self::MAX_EXP + 1 && r.sig == [1 << (Self::PRECISION - 1)] { - // Exponent, significand meaningless. - r.category = Category::Infinity; - } else if r.exp == Self::MAX_EXP + 1 && r.sig != [1 << (Self::PRECISION - 1)] { - // Sign, exponent, significand meaningless. - r.category = Category::NaN; - } else { - r.category = Category::Normal; - if r.exp == Self::MIN_EXP - 1 { - // Denormal. - r.exp = Self::MIN_EXP; - } - } - - r - } - - fn to_bits(x: IeeeFloat) -> u128 { - // Get integer bit from significand. - let integer_bit = sig::get_bit(&x.sig, Self::PRECISION - 1); - let mut significand = x.sig[0] & ((1 << Self::PRECISION) - 1); - let exponent = match x.category { - Category::Normal => { - if x.exp == Self::MIN_EXP && !integer_bit { - // Denormal. - Self::MIN_EXP - 1 - } else { - x.exp - } - } - Category::Zero => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 0; - Self::MIN_EXP - 1 - } - Category::Infinity => { - // FIXME(eddyb) Maybe we should guarantee an invariant instead? - significand = 1 << (Self::PRECISION - 1); - Self::MAX_EXP + 1 - } - Category::NaN => Self::MAX_EXP + 1, - }; - - // Convert the exponent from a signed integer to its bias representation. - let exponent = (exponent + Self::MAX_EXP) as u128; - - ((x.sign as u128) << (Self::BITS - 1)) | (exponent << Self::PRECISION) | significand - } -} - -float_common_impls!(IeeeFloat); - -impl PartialEq for IeeeFloat { - fn eq(&self, rhs: &Self) -> bool { - self.partial_cmp(rhs) == Some(Ordering::Equal) - } -} - -impl PartialOrd for IeeeFloat { - fn partial_cmp(&self, rhs: &Self) -> Option { - match (self.category, rhs.category) { - (Category::NaN, _) | (_, Category::NaN) => None, - - (Category::Infinity, Category::Infinity) => Some((!self.sign).cmp(&(!rhs.sign))), - - (Category::Zero, Category::Zero) => Some(Ordering::Equal), - - (Category::Infinity, _) | (Category::Normal, Category::Zero) => { - Some((!self.sign).cmp(&self.sign)) - } - - (_, Category::Infinity) | (Category::Zero, Category::Normal) => { - Some(rhs.sign.cmp(&(!rhs.sign))) - } - - (Category::Normal, Category::Normal) => { - // Two normal numbers. Do they have the same sign? - Some((!self.sign).cmp(&(!rhs.sign)).then_with(|| { - // Compare absolute values; invert result if negative. - let result = self.cmp_abs_normal(*rhs); - - if self.sign { result.reverse() } else { result } - })) - } - } - } -} - -impl Neg for IeeeFloat { - type Output = Self; - fn neg(mut self) -> Self { - self.sign = !self.sign; - self - } -} - -/// Prints this value as a decimal string. -/// -/// \param precision The maximum number of digits of -/// precision to output. If there are fewer digits available, -/// zero padding will not be used unless the value is -/// integral and small enough to be expressed in -/// precision digits. 0 means to use the natural -/// precision of the number. -/// \param width The maximum number of zeros to -/// consider inserting before falling back to scientific -/// notation. 0 means to always use scientific notation. -/// -/// \param alternate Indicate whether to remove the trailing zero in -/// fraction part or not. Also setting this parameter to true forces -/// producing of output more similar to default printf behavior. -/// Specifically the lower e is used as exponent delimiter and exponent -/// always contains no less than two digits. -/// -/// Number precision width Result -/// ------ --------- ----- ------ -/// 1.01E+4 5 2 10100 -/// 1.01E+4 4 2 1.01E+4 -/// 1.01E+4 5 1 1.01E+4 -/// 1.01E-2 5 2 0.0101 -/// 1.01E-2 4 2 0.0101 -/// 1.01E-2 4 1 1.01E-2 -impl fmt::Display for IeeeFloat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let width = f.width().unwrap_or(3); - let alternate = f.alternate(); - - match self.category { - Category::Infinity => { - if self.sign { - return f.write_str("-Inf"); - } else { - return f.write_str("+Inf"); - } - } - - Category::NaN => return f.write_str("NaN"), - - Category::Zero => { - if self.sign { - f.write_char('-')?; - } - - if width == 0 { - if alternate { - f.write_str("0.0")?; - if let Some(n) = f.precision() { - for _ in 1..n { - f.write_char('0')?; - } - } - f.write_str("e+00")?; - } else { - f.write_str("0.0E+0")?; - } - } else { - f.write_char('0')?; - } - return Ok(()); - } - - Category::Normal => {} - } - - if self.sign { - f.write_char('-')?; - } - - // We use enough digits so the number can be round-tripped back to an - // APFloat. The formula comes from "How to Print Floating-Point Numbers - // Accurately" by Steele and White. - // FIXME: Using a formula based purely on the precision is conservative; - // we can print fewer digits depending on the actual value being printed. - - // precision = 2 + floor(S::PRECISION / lg_2(10)) - let precision = f.precision().unwrap_or(2 + S::PRECISION * 59 / 196); - - // Decompose the number into an APInt and an exponent. - let mut exp = self.exp - (S::PRECISION as ExpInt - 1); - let mut sig = vec![self.sig[0]]; - - // Ignore trailing binary zeros. - let trailing_zeros = sig[0].trailing_zeros(); - let _: Loss = sig::shift_right(&mut sig, &mut exp, trailing_zeros as usize); - - // Change the exponent from 2^e to 10^e. - if exp == 0 { - // Nothing to do. - } else if exp > 0 { - // Just shift left. - let shift = exp as usize; - sig.resize(limbs_for_bits(S::PRECISION + shift), 0); - sig::shift_left(&mut sig, &mut exp, shift); - } else { - // exp < 0 - let mut texp = -exp as usize; - - // We transform this using the identity: - // (N)(2^-e) == (N)(5^e)(10^-e) - - // Multiply significand by 5^e. - // N * 5^0101 == N * 5^(1*1) * 5^(0*2) * 5^(1*4) * 5^(0*8) - let mut sig_scratch = vec![]; - let mut p5 = vec![]; - let mut p5_scratch = vec![]; - while texp != 0 { - if p5.is_empty() { - p5.push(5); - } else { - p5_scratch.resize(p5.len() * 2, 0); - let _: Loss = - sig::mul(&mut p5_scratch, &mut 0, &p5, &p5, p5.len() * 2 * LIMB_BITS); - while p5_scratch.last() == Some(&0) { - p5_scratch.pop(); - } - mem::swap(&mut p5, &mut p5_scratch); - } - if texp & 1 != 0 { - sig_scratch.resize(sig.len() + p5.len(), 0); - let _: Loss = sig::mul( - &mut sig_scratch, - &mut 0, - &sig, - &p5, - (sig.len() + p5.len()) * LIMB_BITS, - ); - while sig_scratch.last() == Some(&0) { - sig_scratch.pop(); - } - mem::swap(&mut sig, &mut sig_scratch); - } - texp >>= 1; - } - } - - // Fill the buffer. - let mut buffer = vec![]; - - // Ignore digits from the significand until it is no more - // precise than is required for the desired precision. - // 196/59 is a very slight overestimate of lg_2(10). - let required = (precision * 196 + 58) / 59; - let mut discard_digits = sig::omsb(&sig).saturating_sub(required) * 59 / 196; - let mut in_trail = true; - while !sig.is_empty() { - // Perform short division by 10 to extract the rightmost digit. - // rem <- sig % 10 - // sig <- sig / 10 - let mut rem = 0; - - // Use 64-bit division and remainder, with 32-bit chunks from sig. - sig::each_chunk(&mut sig, 32, |chunk| { - let chunk = chunk as u32; - let combined = ((rem as u64) << 32) | (chunk as u64); - rem = (combined % 10) as u8; - (combined / 10) as u32 as Limb - }); - - // Reduce the significand to avoid wasting time dividing 0's. - while sig.last() == Some(&0) { - sig.pop(); - } - - let digit = rem; - - // Ignore digits we don't need. - if discard_digits > 0 { - discard_digits -= 1; - exp += 1; - continue; - } - - // Drop trailing zeros. - if in_trail && digit == 0 { - exp += 1; - } else { - in_trail = false; - buffer.push(b'0' + digit); - } - } - - assert!(!buffer.is_empty(), "no characters in buffer!"); - - // Drop down to precision. - // FIXME: don't do more precise calculations above than are required. - if buffer.len() > precision { - // The most significant figures are the last ones in the buffer. - let mut first_sig = buffer.len() - precision; - - // Round. - // FIXME: this probably shouldn't use 'round half up'. - - // Rounding down is just a truncation, except we also want to drop - // trailing zeros from the new result. - if buffer[first_sig - 1] < b'5' { - while first_sig < buffer.len() && buffer[first_sig] == b'0' { - first_sig += 1; - } - } else { - // Rounding up requires a decimal add-with-carry. If we continue - // the carry, the newly-introduced zeros will just be truncated. - for x in &mut buffer[first_sig..] { - if *x == b'9' { - first_sig += 1; - } else { - *x += 1; - break; - } - } - } - - exp += first_sig as ExpInt; - buffer.drain(..first_sig); - - // If we carried through, we have exactly one digit of precision. - if buffer.is_empty() { - buffer.push(b'1'); - } - } - - let digits = buffer.len(); - - // Check whether we should use scientific notation. - let scientific = if width == 0 { - true - } else if exp >= 0 { - // 765e3 --> 765000 - // ^^^ - // But we shouldn't make the number look more precise than it is. - exp as usize > width || digits + exp as usize > precision - } else { - // Power of the most significant digit. - let msd = exp + (digits - 1) as ExpInt; - if msd >= 0 { - // 765e-2 == 7.65 - false - } else { - // 765e-5 == 0.00765 - // ^ ^^ - -msd as usize > width - } - }; - - // Scientific formatting is pretty straightforward. - if scientific { - exp += digits as ExpInt - 1; - - f.write_char(buffer[digits - 1] as char)?; - f.write_char('.')?; - let truncate_zero = !alternate; - if digits == 1 && truncate_zero { - f.write_char('0')?; - } else { - for &d in buffer[..digits - 1].iter().rev() { - f.write_char(d as char)?; - } - } - // Fill with zeros up to precision. - if !truncate_zero && precision > digits - 1 { - for _ in 0..=precision - digits { - f.write_char('0')?; - } - } - // For alternate we use lower 'e'. - f.write_char(if alternate { 'e' } else { 'E' })?; - - // Exponent always at least two digits if we do not truncate zeros. - if truncate_zero { - write!(f, "{:+}", exp)?; - } else { - write!(f, "{:+03}", exp)?; - } - - return Ok(()); - } - - // Non-scientific, positive exponents. - if exp >= 0 { - for &d in buffer.iter().rev() { - f.write_char(d as char)?; - } - for _ in 0..exp { - f.write_char('0')?; - } - return Ok(()); - } - - // Non-scientific, negative exponents. - let unit_place = -exp as usize; - if unit_place < digits { - for &d in buffer[unit_place..].iter().rev() { - f.write_char(d as char)?; - } - f.write_char('.')?; - for &d in buffer[..unit_place].iter().rev() { - f.write_char(d as char)?; - } - } else { - f.write_str("0.")?; - for _ in digits..unit_place { - f.write_char('0')?; - } - for &d in buffer.iter().rev() { - f.write_char(d as char)?; - } - } - - Ok(()) - } -} - -impl fmt::Debug for IeeeFloat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}({:?} | {}{:?} * 2^{})", - self, - self.category, - if self.sign { "-" } else { "+" }, - self.sig, - self.exp - ) - } -} - -impl Float for IeeeFloat { - const BITS: usize = S::BITS; - const PRECISION: usize = S::PRECISION; - const MAX_EXP: ExpInt = S::MAX_EXP; - const MIN_EXP: ExpInt = S::MIN_EXP; - - const ZERO: Self = IeeeFloat { - sig: [0], - exp: S::MIN_EXP - 1, - category: Category::Zero, - sign: false, - marker: PhantomData, - }; - - const INFINITY: Self = IeeeFloat { - sig: [0], - exp: S::MAX_EXP + 1, - category: Category::Infinity, - sign: false, - marker: PhantomData, - }; - - // FIXME(eddyb) remove when qnan becomes const fn. - const NAN: Self = IeeeFloat { - sig: [S::QNAN_SIGNIFICAND], - exp: S::MAX_EXP + 1, - category: Category::NaN, - sign: false, - marker: PhantomData, - }; - - fn qnan(payload: Option) -> Self { - IeeeFloat { - sig: [S::QNAN_SIGNIFICAND - | payload.map_or(0, |payload| { - // Zero out the excess bits of the significand. - payload & ((1 << S::QNAN_BIT) - 1) - })], - exp: S::MAX_EXP + 1, - category: Category::NaN, - sign: false, - marker: PhantomData, - } - } - - fn snan(payload: Option) -> Self { - let mut snan = Self::qnan(payload); - - // We always have to clear the QNaN bit to make it an SNaN. - sig::clear_bit(&mut snan.sig, S::QNAN_BIT); - - // If there are no bits set in the payload, we have to set - // *something* to make it a NaN instead of an infinity; - // conventionally, this is the next bit down from the QNaN bit. - if snan.sig[0] & !S::QNAN_SIGNIFICAND == 0 { - sig::set_bit(&mut snan.sig, S::QNAN_BIT - 1); - } - - snan - } - - fn largest() -> Self { - // We want (in interchange format): - // exponent = 1..10 - // significand = 1..1 - IeeeFloat { - sig: [(1 << S::PRECISION) - 1], - exp: S::MAX_EXP, - category: Category::Normal, - sign: false, - marker: PhantomData, - } - } - - // We want (in interchange format): - // exponent = 0..0 - // significand = 0..01 - const SMALLEST: Self = IeeeFloat { - sig: [1], - exp: S::MIN_EXP, - category: Category::Normal, - sign: false, - marker: PhantomData, - }; - - fn smallest_normalized() -> Self { - // We want (in interchange format): - // exponent = 0..0 - // significand = 10..0 - IeeeFloat { - sig: [1 << (S::PRECISION - 1)], - exp: S::MIN_EXP, - category: Category::Normal, - sign: false, - marker: PhantomData, - } - } - - fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd { - let status = match (self.category, rhs.category) { - (Category::Infinity, Category::Infinity) => { - // Differently signed infinities can only be validly - // subtracted. - if self.sign != rhs.sign { - self = Self::NAN; - Status::INVALID_OP - } else { - Status::OK - } - } - - // Sign may depend on rounding mode; handled below. - (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => { - Status::OK - } - - (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => { - self = rhs; - Status::OK - } - - // This return code means it was not a simple case. - (Category::Normal, Category::Normal) => { - let loss = sig::add_or_sub( - &mut self.sig, - &mut self.exp, - &mut self.sign, - &mut [rhs.sig[0]], - rhs.exp, - rhs.sign, - ); - let status; - self = unpack!(status=, self.normalize(round, loss)); - - // Can only be zero if we lost no fraction. - assert!(self.category != Category::Zero || loss == Loss::ExactlyZero); - - status - } - }; - - // If two numbers add (exactly) to zero, IEEE 754 decrees it is a - // positive zero unless rounding to minus infinity, except that - // adding two like-signed zeroes gives that zero. - if self.category == Category::Zero - && (rhs.category != Category::Zero || self.sign != rhs.sign) - { - self.sign = round == Round::TowardNegative; - } - - status.and(self) - } - - fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd { - self.sign ^= rhs.sign; - - match (self.category, rhs.category) { - (Category::NaN, _) => { - self.sign = false; - Status::OK.and(self) - } - - (_, Category::NaN) => { - self.sign = false; - self.category = Category::NaN; - self.sig = rhs.sig; - Status::OK.and(self) - } - - (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => { - Status::INVALID_OP.and(Self::NAN) - } - - (_, Category::Infinity) | (Category::Infinity, _) => { - self.category = Category::Infinity; - Status::OK.and(self) - } - - (Category::Zero, _) | (_, Category::Zero) => { - self.category = Category::Zero; - Status::OK.and(self) - } - - (Category::Normal, Category::Normal) => { - self.exp += rhs.exp; - let mut wide_sig = [0; 2]; - let loss = - sig::mul(&mut wide_sig, &mut self.exp, &self.sig, &rhs.sig, S::PRECISION); - self.sig = [wide_sig[0]]; - let mut status; - self = unpack!(status=, self.normalize(round, loss)); - if loss != Loss::ExactlyZero { - status |= Status::INEXACT; - } - status.and(self) - } - } - } - - fn mul_add_r(mut self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd { - // If and only if all arguments are normal do we need to do an - // extended-precision calculation. - if !self.is_finite_non_zero() || !multiplicand.is_finite_non_zero() || !addend.is_finite() { - let mut status; - self = unpack!(status=, self.mul_r(multiplicand, round)); - - // FS can only be Status::OK or Status::INVALID_OP. There is no more work - // to do in the latter case. The IEEE-754R standard says it is - // implementation-defined in this case whether, if ADDEND is a - // quiet NaN, we raise invalid op; this implementation does so. - // - // If we need to do the addition we can do so with normal - // precision. - if status == Status::OK { - self = unpack!(status=, self.add_r(addend, round)); - } - return status.and(self); - } - - // Post-multiplication sign, before addition. - self.sign ^= multiplicand.sign; - - // Allocate space for twice as many bits as the original significand, plus one - // extra bit for the addition to overflow into. - assert!(limbs_for_bits(S::PRECISION * 2 + 1) <= 2); - let mut wide_sig = sig::widening_mul(self.sig[0], multiplicand.sig[0]); - - let mut loss = Loss::ExactlyZero; - let mut omsb = sig::omsb(&wide_sig); - self.exp += multiplicand.exp; - - // Assume the operands involved in the multiplication are single-precision - // FP, and the two multiplicants are: - // lhs = a23 . a22 ... a0 * 2^e1 - // rhs = b23 . b22 ... b0 * 2^e2 - // the result of multiplication is: - // lhs = c48 c47 c46 . c45 ... c0 * 2^(e1+e2) - // Note that there are three significant bits at the left-hand side of the - // radix point: two for the multiplication, and an overflow bit for the - // addition (that will always be zero at this point). Move the radix point - // toward left by two bits, and adjust exponent accordingly. - self.exp += 2; - - if addend.is_non_zero() { - // Normalize our MSB to one below the top bit to allow for overflow. - let ext_precision = 2 * S::PRECISION + 1; - if omsb != ext_precision - 1 { - assert!(ext_precision > omsb); - sig::shift_left(&mut wide_sig, &mut self.exp, (ext_precision - 1) - omsb); - } - - // The intermediate result of the multiplication has "2 * S::PRECISION" - // significant bit; adjust the addend to be consistent with mul result. - let mut ext_addend_sig = [addend.sig[0], 0]; - - // Extend the addend significand to ext_precision - 1. This guarantees - // that the high bit of the significand is zero (same as wide_sig), - // so the addition will overflow (if it does overflow at all) into the top bit. - sig::shift_left(&mut ext_addend_sig, &mut 0, ext_precision - 1 - S::PRECISION); - loss = sig::add_or_sub( - &mut wide_sig, - &mut self.exp, - &mut self.sign, - &mut ext_addend_sig, - addend.exp + 1, - addend.sign, - ); - - omsb = sig::omsb(&wide_sig); - } - - // Convert the result having "2 * S::PRECISION" significant-bits back to the one - // having "S::PRECISION" significant-bits. First, move the radix point from - // position "2*S::PRECISION - 1" to "S::PRECISION - 1". The exponent need to be - // adjusted by "2*S::PRECISION - 1" - "S::PRECISION - 1" = "S::PRECISION". - self.exp -= S::PRECISION as ExpInt + 1; - - // In case MSB resides at the left-hand side of radix point, shift the - // mantissa right by some amount to make sure the MSB reside right before - // the radix point (i.e., "MSB . rest-significant-bits"). - if omsb > S::PRECISION { - let bits = omsb - S::PRECISION; - loss = sig::shift_right(&mut wide_sig, &mut self.exp, bits).combine(loss); - } - - self.sig[0] = wide_sig[0]; - - let mut status; - self = unpack!(status=, self.normalize(round, loss)); - if loss != Loss::ExactlyZero { - status |= Status::INEXACT; - } - - // If two numbers add (exactly) to zero, IEEE 754 decrees it is a - // positive zero unless rounding to minus infinity, except that - // adding two like-signed zeroes gives that zero. - if self.category == Category::Zero - && !status.intersects(Status::UNDERFLOW) - && self.sign != addend.sign - { - self.sign = round == Round::TowardNegative; - } - - status.and(self) - } - - fn div_r(mut self, rhs: Self, round: Round) -> StatusAnd { - self.sign ^= rhs.sign; - - match (self.category, rhs.category) { - (Category::NaN, _) => { - self.sign = false; - Status::OK.and(self) - } - - (_, Category::NaN) => { - self.category = Category::NaN; - self.sig = rhs.sig; - self.sign = false; - Status::OK.and(self) - } - - (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => { - Status::INVALID_OP.and(Self::NAN) - } - - (Category::Infinity | Category::Zero, _) => Status::OK.and(self), - - (Category::Normal, Category::Infinity) => { - self.category = Category::Zero; - Status::OK.and(self) - } - - (Category::Normal, Category::Zero) => { - self.category = Category::Infinity; - Status::DIV_BY_ZERO.and(self) - } - - (Category::Normal, Category::Normal) => { - self.exp -= rhs.exp; - let dividend = self.sig[0]; - let loss = sig::div( - &mut self.sig, - &mut self.exp, - &mut [dividend], - &mut [rhs.sig[0]], - S::PRECISION, - ); - let mut status; - self = unpack!(status=, self.normalize(round, loss)); - if loss != Loss::ExactlyZero { - status |= Status::INEXACT; - } - status.and(self) - } - } - } - - fn c_fmod(mut self, rhs: Self) -> StatusAnd { - match (self.category, rhs.category) { - (Category::NaN, _) - | (Category::Zero, Category::Infinity | Category::Normal) - | (Category::Normal, Category::Infinity) => Status::OK.and(self), - - (_, Category::NaN) => { - self.sign = false; - self.category = Category::NaN; - self.sig = rhs.sig; - Status::OK.and(self) - } - - (Category::Infinity, _) | (_, Category::Zero) => Status::INVALID_OP.and(Self::NAN), - - (Category::Normal, Category::Normal) => { - while self.is_finite_non_zero() - && rhs.is_finite_non_zero() - && self.cmp_abs_normal(rhs) != Ordering::Less - { - let mut v = rhs.scalbn(self.ilogb() - rhs.ilogb()); - if self.cmp_abs_normal(v) == Ordering::Less { - v = v.scalbn(-1); - } - v.sign = self.sign; - - let status; - self = unpack!(status=, self - v); - assert_eq!(status, Status::OK); - } - Status::OK.and(self) - } - } - } - - fn round_to_integral(self, round: Round) -> StatusAnd { - // If the exponent is large enough, we know that this value is already - // integral, and the arithmetic below would potentially cause it to saturate - // to +/-Inf. Bail out early instead. - if self.is_finite_non_zero() && self.exp + 1 >= S::PRECISION as ExpInt { - return Status::OK.and(self); - } - - // The algorithm here is quite simple: we add 2^(p-1), where p is the - // precision of our format, and then subtract it back off again. The choice - // of rounding modes for the addition/subtraction determines the rounding mode - // for our integral rounding as well. - // NOTE: When the input value is negative, we do subtraction followed by - // addition instead. - assert!(S::PRECISION <= 128); - let mut status; - let magic_const = unpack!(status=, Self::from_u128(1 << (S::PRECISION - 1))); - let magic_const = magic_const.copy_sign(self); - - if status != Status::OK { - return status.and(self); - } - - let mut r = self; - r = unpack!(status=, r.add_r(magic_const, round)); - if status != Status::OK && status != Status::INEXACT { - return status.and(self); - } - - // Restore the input sign to handle 0.0/-0.0 cases correctly. - r.sub_r(magic_const, round).map(|r| r.copy_sign(self)) - } - - fn next_up(mut self) -> StatusAnd { - // Compute nextUp(x), handling each float category separately. - match self.category { - Category::Infinity => { - if self.sign { - // nextUp(-inf) = -largest - Status::OK.and(-Self::largest()) - } else { - // nextUp(+inf) = +inf - Status::OK.and(self) - } - } - Category::NaN => { - // IEEE-754R 2008 6.2 Par 2: nextUp(sNaN) = qNaN. Set Invalid flag. - // IEEE-754R 2008 6.2: nextUp(qNaN) = qNaN. Must be identity so we do not - // change the payload. - if self.is_signaling() { - // For consistency, propagate the sign of the sNaN to the qNaN. - Status::INVALID_OP.and(Self::NAN.copy_sign(self)) - } else { - Status::OK.and(self) - } - } - Category::Zero => { - // nextUp(pm 0) = +smallest - Status::OK.and(Self::SMALLEST) - } - Category::Normal => { - // nextUp(-smallest) = -0 - if self.is_smallest() && self.sign { - return Status::OK.and(-Self::ZERO); - } - - // nextUp(largest) == INFINITY - if self.is_largest() && !self.sign { - return Status::OK.and(Self::INFINITY); - } - - // Excluding the integral bit. This allows us to test for binade boundaries. - let sig_mask = (1 << (S::PRECISION - 1)) - 1; - - // nextUp(normal) == normal + inc. - if self.sign { - // If we are negative, we need to decrement the significand. - - // We only cross a binade boundary that requires adjusting the exponent - // if: - // 1. exponent != S::MIN_EXP. This implies we are not in the - // smallest binade or are dealing with denormals. - // 2. Our significand excluding the integral bit is all zeros. - let crossing_binade_boundary = - self.exp != S::MIN_EXP && self.sig[0] & sig_mask == 0; - - // Decrement the significand. - // - // We always do this since: - // 1. If we are dealing with a non-binade decrement, by definition we - // just decrement the significand. - // 2. If we are dealing with a normal -> normal binade decrement, since - // we have an explicit integral bit the fact that all bits but the - // integral bit are zero implies that subtracting one will yield a - // significand with 0 integral bit and 1 in all other spots. Thus we - // must just adjust the exponent and set the integral bit to 1. - // 3. If we are dealing with a normal -> denormal binade decrement, - // since we set the integral bit to 0 when we represent denormals, we - // just decrement the significand. - sig::decrement(&mut self.sig); - - if crossing_binade_boundary { - // Our result is a normal number. Do the following: - // 1. Set the integral bit to 1. - // 2. Decrement the exponent. - sig::set_bit(&mut self.sig, S::PRECISION - 1); - self.exp -= 1; - } - } else { - // If we are positive, we need to increment the significand. - - // We only cross a binade boundary that requires adjusting the exponent if - // the input is not a denormal and all of said input's significand bits - // are set. If all of said conditions are true: clear the significand, set - // the integral bit to 1, and increment the exponent. If we have a - // denormal always increment since moving denormals and the numbers in the - // smallest normal binade have the same exponent in our representation. - let crossing_binade_boundary = - !self.is_denormal() && self.sig[0] & sig_mask == sig_mask; - - if crossing_binade_boundary { - self.sig = [0]; - sig::set_bit(&mut self.sig, S::PRECISION - 1); - assert_ne!( - self.exp, - S::MAX_EXP, - "We can not increment an exponent beyond the MAX_EXP \ - allowed by the given floating point semantics." - ); - self.exp += 1; - } else { - sig::increment(&mut self.sig); - } - } - Status::OK.and(self) - } - } - } - - fn from_bits(input: u128) -> Self { - // Dispatch to semantics. - S::from_bits(input) - } - - fn from_u128_r(input: u128, round: Round) -> StatusAnd { - IeeeFloat { - sig: [input], - exp: S::PRECISION as ExpInt - 1, - category: Category::Normal, - sign: false, - marker: PhantomData, - } - .normalize(round, Loss::ExactlyZero) - } - - fn from_str_r(mut s: &str, mut round: Round) -> Result, ParseError> { - if s.is_empty() { - return Err(ParseError("Invalid string length")); - } - - // Handle special cases. - match s { - "inf" | "INFINITY" => return Ok(Status::OK.and(Self::INFINITY)), - "-inf" | "-INFINITY" => return Ok(Status::OK.and(-Self::INFINITY)), - "nan" | "NaN" => return Ok(Status::OK.and(Self::NAN)), - "-nan" | "-NaN" => return Ok(Status::OK.and(-Self::NAN)), - _ => {} - } - - // Handle a leading minus sign. - let minus = s.starts_with('-'); - if minus || s.starts_with('+') { - s = &s[1..]; - if s.is_empty() { - return Err(ParseError("String has no digits")); - } - } - - // Adjust the rounding mode for the absolute value below. - if minus { - round = -round; - } - - let r = if s.starts_with("0x") || s.starts_with("0X") { - s = &s[2..]; - if s.is_empty() { - return Err(ParseError("Invalid string")); - } - Self::from_hexadecimal_string(s, round)? - } else { - Self::from_decimal_string(s, round)? - }; - - Ok(r.map(|r| if minus { -r } else { r })) - } - - fn to_bits(self) -> u128 { - // Dispatch to semantics. - S::to_bits(self) - } - - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd { - // The result of trying to convert a number too large. - let overflow = if self.sign { - // Negative numbers cannot be represented as unsigned. - 0 - } else { - // Largest unsigned integer of the given width. - !0 >> (128 - width) - }; - - *is_exact = false; - - match self.category { - Category::NaN => Status::INVALID_OP.and(0), - - Category::Infinity => Status::INVALID_OP.and(overflow), - - Category::Zero => { - // Negative zero can't be represented as an int. - *is_exact = !self.sign; - Status::OK.and(0) - } - - Category::Normal => { - let mut r = 0; - - // Step 1: place our absolute value, with any fraction truncated, in - // the destination. - let truncated_bits = if self.exp < 0 { - // Our absolute value is less than one; truncate everything. - // For exponent -1 the integer bit represents .5, look at that. - // For smaller exponents leftmost truncated bit is 0. - S::PRECISION - 1 + (-self.exp) as usize - } else { - // We want the most significant (exponent + 1) bits; the rest are - // truncated. - let bits = self.exp as usize + 1; - - // Hopelessly large in magnitude? - if bits > width { - return Status::INVALID_OP.and(overflow); - } - - if bits < S::PRECISION { - // We truncate (S::PRECISION - bits) bits. - r = self.sig[0] >> (S::PRECISION - bits); - S::PRECISION - bits - } else { - // We want at least as many bits as are available. - r = self.sig[0] << (bits - S::PRECISION); - 0 - } - }; - - // Step 2: work out any lost fraction, and increment the absolute - // value if we would round away from zero. - let mut loss = Loss::ExactlyZero; - if truncated_bits > 0 { - loss = Loss::through_truncation(&self.sig, truncated_bits); - if loss != Loss::ExactlyZero - && self.round_away_from_zero(round, loss, truncated_bits) - { - r = r.wrapping_add(1); - if r == 0 { - return Status::INVALID_OP.and(overflow); // Overflow. - } - } - } - - // Step 3: check if we fit in the destination. - if r > overflow { - return Status::INVALID_OP.and(overflow); - } - - if loss == Loss::ExactlyZero { - *is_exact = true; - Status::OK.and(r) - } else { - Status::INEXACT.and(r) - } - } - } - } - - fn cmp_abs_normal(self, rhs: Self) -> Ordering { - assert!(self.is_finite_non_zero()); - assert!(rhs.is_finite_non_zero()); - - // If exponents are equal, do an unsigned comparison of the significands. - self.exp.cmp(&rhs.exp).then_with(|| sig::cmp(&self.sig, &rhs.sig)) - } - - fn bitwise_eq(self, rhs: Self) -> bool { - if self.category != rhs.category || self.sign != rhs.sign { - return false; - } - - if self.category == Category::Zero || self.category == Category::Infinity { - return true; - } - - if self.is_finite_non_zero() && self.exp != rhs.exp { - return false; - } - - self.sig == rhs.sig - } - - fn is_negative(self) -> bool { - self.sign - } - - fn is_denormal(self) -> bool { - self.is_finite_non_zero() - && self.exp == S::MIN_EXP - && !sig::get_bit(&self.sig, S::PRECISION - 1) - } - - fn is_signaling(self) -> bool { - // IEEE-754R 2008 6.2.1: A signaling NaN bit string should be encoded with the - // first bit of the trailing significand being 0. - self.is_nan() && !sig::get_bit(&self.sig, S::QNAN_BIT) - } - - fn category(self) -> Category { - self.category - } - - fn get_exact_inverse(self) -> Option { - // Special floats and denormals have no exact inverse. - if !self.is_finite_non_zero() { - return None; - } - - // Check that the number is a power of two by making sure that only the - // integer bit is set in the significand. - if self.sig != [1 << (S::PRECISION - 1)] { - return None; - } - - // Get the inverse. - let mut reciprocal = Self::from_u128(1).value; - let status; - reciprocal = unpack!(status=, reciprocal / self); - if status != Status::OK { - return None; - } - - // Avoid multiplication with a denormal, it is not safe on all platforms and - // may be slower than a normal division. - if reciprocal.is_denormal() { - return None; - } - - assert!(reciprocal.is_finite_non_zero()); - assert_eq!(reciprocal.sig, [1 << (S::PRECISION - 1)]); - - Some(reciprocal) - } - - fn ilogb(mut self) -> ExpInt { - if self.is_nan() { - return IEK_NAN; - } - if self.is_zero() { - return IEK_ZERO; - } - if self.is_infinite() { - return IEK_INF; - } - if !self.is_denormal() { - return self.exp; - } - - let sig_bits = (S::PRECISION - 1) as ExpInt; - self.exp += sig_bits; - self = self.normalize(Round::NearestTiesToEven, Loss::ExactlyZero).value; - self.exp - sig_bits - } - - fn scalbn_r(mut self, exp: ExpInt, round: Round) -> Self { - // If exp is wildly out-of-scale, simply adding it to self.exp will - // overflow; clamp it to a safe range before adding, but ensure that the range - // is large enough that the clamp does not change the result. The range we - // need to support is the difference between the largest possible exponent and - // the normalized exponent of half the smallest denormal. - - let sig_bits = (S::PRECISION - 1) as i32; - let max_change = S::MAX_EXP as i32 - (S::MIN_EXP as i32 - sig_bits) + 1; - - // Clamp to one past the range ends to let normalize handle overflow. - let exp_change = cmp::min(cmp::max(exp as i32, -max_change - 1), max_change); - self.exp = self.exp.saturating_add(exp_change as ExpInt); - self = self.normalize(round, Loss::ExactlyZero).value; - if self.is_nan() { - sig::set_bit(&mut self.sig, S::QNAN_BIT); - } - self - } - - fn frexp_r(mut self, exp: &mut ExpInt, round: Round) -> Self { - *exp = self.ilogb(); - - // Quiet signalling nans. - if *exp == IEK_NAN { - sig::set_bit(&mut self.sig, S::QNAN_BIT); - return self; - } - - if *exp == IEK_INF { - return self; - } - - // 1 is added because frexp is defined to return a normalized fraction in - // +/-[0.5, 1.0), rather than the usual +/-[1.0, 2.0). - if *exp == IEK_ZERO { - *exp = 0; - } else { - *exp += 1; - } - self.scalbn_r(-*exp, round) - } -} - -impl FloatConvert> for IeeeFloat { - fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd> { - let mut r = IeeeFloat { - sig: self.sig, - exp: self.exp, - category: self.category, - sign: self.sign, - marker: PhantomData, - }; - - // x86 has some unusual NaNs which cannot be represented in any other - // format; note them here. - fn is_x87_double_extended() -> bool { - S::QNAN_SIGNIFICAND == X87DoubleExtendedS::QNAN_SIGNIFICAND - } - let x87_special_nan = is_x87_double_extended::() - && !is_x87_double_extended::() - && r.category == Category::NaN - && (r.sig[0] & S::QNAN_SIGNIFICAND) != S::QNAN_SIGNIFICAND; - - // If this is a truncation of a denormal number, and the target semantics - // has larger exponent range than the source semantics (this can happen - // when truncating from PowerPC double-double to double format), the - // right shift could lose result mantissa bits. Adjust exponent instead - // of performing excessive shift. - let mut shift = T::PRECISION as ExpInt - S::PRECISION as ExpInt; - if shift < 0 && r.is_finite_non_zero() { - let mut exp_change = sig::omsb(&r.sig) as ExpInt - S::PRECISION as ExpInt; - if r.exp + exp_change < T::MIN_EXP { - exp_change = T::MIN_EXP - r.exp; - } - if exp_change < shift { - exp_change = shift; - } - if exp_change < 0 { - shift -= exp_change; - r.exp += exp_change; - } - } - - // If this is a truncation, perform the shift. - let loss = if shift < 0 && (r.is_finite_non_zero() || r.category == Category::NaN) { - sig::shift_right(&mut r.sig, &mut 0, -shift as usize) - } else { - Loss::ExactlyZero - }; - - // If this is an extension, perform the shift. - if shift > 0 && (r.is_finite_non_zero() || r.category == Category::NaN) { - sig::shift_left(&mut r.sig, &mut 0, shift as usize); - } - - let status; - if r.is_finite_non_zero() { - r = unpack!(status=, r.normalize(round, loss)); - *loses_info = status != Status::OK; - } else if r.category == Category::NaN { - *loses_info = loss != Loss::ExactlyZero || x87_special_nan; - - // For x87 extended precision, we want to make a NaN, not a special NaN if - // the input wasn't special either. - if !x87_special_nan && is_x87_double_extended::() { - sig::set_bit(&mut r.sig, T::PRECISION - 1); - } - - // Convert of sNaN creates qNaN and raises an exception (invalid op). - // This also guarantees that a sNaN does not become Inf on a truncation - // that loses all payload bits. - if self.is_signaling() { - // Quiet signaling NaN. - sig::set_bit(&mut r.sig, T::QNAN_BIT); - status = Status::INVALID_OP; - } else { - status = Status::OK; - } - } else { - *loses_info = false; - status = Status::OK; - } - - status.and(r) - } -} - -impl IeeeFloat { - /// Handle positive overflow. We either return infinity or - /// the largest finite number. For negative overflow, - /// negate the `round` argument before calling. - fn overflow_result(round: Round) -> StatusAnd { - match round { - // Infinity? - Round::NearestTiesToEven | Round::NearestTiesToAway | Round::TowardPositive => { - (Status::OVERFLOW | Status::INEXACT).and(Self::INFINITY) - } - // Otherwise we become the largest finite number. - Round::TowardNegative | Round::TowardZero => Status::INEXACT.and(Self::largest()), - } - } - - /// Returns `true` if, when truncating the current number, with `bit` the - /// new LSB, with the given lost fraction and rounding mode, the result - /// would need to be rounded away from zero (i.e., by increasing the - /// signficand). This routine must work for `Category::Zero` of both signs, and - /// `Category::Normal` numbers. - fn round_away_from_zero(&self, round: Round, loss: Loss, bit: usize) -> bool { - // NaNs and infinities should not have lost fractions. - assert!(self.is_finite_non_zero() || self.is_zero()); - - // Current callers never pass this so we don't handle it. - assert_ne!(loss, Loss::ExactlyZero); - - match round { - Round::NearestTiesToAway => loss == Loss::ExactlyHalf || loss == Loss::MoreThanHalf, - Round::NearestTiesToEven => { - if loss == Loss::MoreThanHalf { - return true; - } - - // Our zeros don't have a significand to test. - if loss == Loss::ExactlyHalf && self.category != Category::Zero { - return sig::get_bit(&self.sig, bit); - } - - false - } - Round::TowardZero => false, - Round::TowardPositive => !self.sign, - Round::TowardNegative => self.sign, - } - } - - fn normalize(mut self, round: Round, mut loss: Loss) -> StatusAnd { - if !self.is_finite_non_zero() { - return Status::OK.and(self); - } - - // Before rounding normalize the exponent of Category::Normal numbers. - let mut omsb = sig::omsb(&self.sig); - - if omsb > 0 { - // OMSB is numbered from 1. We want to place it in the integer - // bit numbered PRECISION if possible, with a compensating change in - // the exponent. - let mut final_exp = self.exp.saturating_add(omsb as ExpInt - S::PRECISION as ExpInt); - - // If the resulting exponent is too high, overflow according to - // the rounding mode. - if final_exp > S::MAX_EXP { - let round = if self.sign { -round } else { round }; - return Self::overflow_result(round).map(|r| r.copy_sign(self)); - } - - // Subnormal numbers have exponent MIN_EXP, and their MSB - // is forced based on that. - if final_exp < S::MIN_EXP { - final_exp = S::MIN_EXP; - } - - // Shifting left is easy as we don't lose precision. - if final_exp < self.exp { - assert_eq!(loss, Loss::ExactlyZero); - - let exp_change = (self.exp - final_exp) as usize; - sig::shift_left(&mut self.sig, &mut self.exp, exp_change); - - return Status::OK.and(self); - } - - // Shift right and capture any new lost fraction. - if final_exp > self.exp { - let exp_change = (final_exp - self.exp) as usize; - loss = sig::shift_right(&mut self.sig, &mut self.exp, exp_change).combine(loss); - - // Keep OMSB up-to-date. - omsb = omsb.saturating_sub(exp_change); - } - } - - // Now round the number according to round given the lost - // fraction. - - // As specified in IEEE 754, since we do not trap we do not report - // underflow for exact results. - if loss == Loss::ExactlyZero { - // Canonicalize zeros. - if omsb == 0 { - self.category = Category::Zero; - } - - return Status::OK.and(self); - } - - // Increment the significand if we're rounding away from zero. - if self.round_away_from_zero(round, loss, 0) { - if omsb == 0 { - self.exp = S::MIN_EXP; - } - - // We should never overflow. - assert_eq!(sig::increment(&mut self.sig), 0); - omsb = sig::omsb(&self.sig); - - // Did the significand increment overflow? - if omsb == S::PRECISION + 1 { - // Renormalize by incrementing the exponent and shifting our - // significand right one. However if we already have the - // maximum exponent we overflow to infinity. - if self.exp == S::MAX_EXP { - self.category = Category::Infinity; - - return (Status::OVERFLOW | Status::INEXACT).and(self); - } - - let _: Loss = sig::shift_right(&mut self.sig, &mut self.exp, 1); - - return Status::INEXACT.and(self); - } - } - - // The normal case - we were and are not denormal, and any - // significand increment above didn't overflow. - if omsb == S::PRECISION { - return Status::INEXACT.and(self); - } - - // We have a non-zero denormal. - assert!(omsb < S::PRECISION); - - // Canonicalize zeros. - if omsb == 0 { - self.category = Category::Zero; - } - - // The Category::Zero case is a denormal that underflowed to zero. - (Status::UNDERFLOW | Status::INEXACT).and(self) - } - - fn from_hexadecimal_string(s: &str, round: Round) -> Result, ParseError> { - let mut r = IeeeFloat { - sig: [0], - exp: 0, - category: Category::Normal, - sign: false, - marker: PhantomData, - }; - - let mut any_digits = false; - let mut has_exp = false; - let mut bit_pos = LIMB_BITS as isize; - let mut loss = None; - - // Without leading or trailing zeros, irrespective of the dot. - let mut first_sig_digit = None; - let mut dot = s.len(); - - for (p, c) in s.char_indices() { - // Skip leading zeros and any (hexa)decimal point. - if c == '.' { - if dot != s.len() { - return Err(ParseError("String contains multiple dots")); - } - dot = p; - } else if let Some(hex_value) = c.to_digit(16) { - any_digits = true; - - if first_sig_digit.is_none() { - if hex_value == 0 { - continue; - } - first_sig_digit = Some(p); - } - - // Store the number while we have space. - bit_pos -= 4; - if bit_pos >= 0 { - r.sig[0] |= (hex_value as Limb) << bit_pos; - // If zero or one-half (the hexadecimal digit 8) are followed - // by non-zero, they're a little more than zero or one-half. - } else if let Some(ref mut loss) = loss { - if hex_value != 0 { - if *loss == Loss::ExactlyZero { - *loss = Loss::LessThanHalf; - } - if *loss == Loss::ExactlyHalf { - *loss = Loss::MoreThanHalf; - } - } - } else { - loss = Some(match hex_value { - 0 => Loss::ExactlyZero, - 1..=7 => Loss::LessThanHalf, - 8 => Loss::ExactlyHalf, - 9..=15 => Loss::MoreThanHalf, - _ => unreachable!(), - }); - } - } else if c == 'p' || c == 'P' { - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - if dot == s.len() { - dot = p; - } - - let mut chars = s[p + 1..].chars().peekable(); - - // Adjust for the given exponent. - let exp_minus = chars.peek() == Some(&'-'); - if exp_minus || chars.peek() == Some(&'+') { - chars.next(); - } - - for c in chars { - if let Some(value) = c.to_digit(10) { - has_exp = true; - r.exp = r.exp.saturating_mul(10).saturating_add(value as ExpInt); - } else { - return Err(ParseError("Invalid character in exponent")); - } - } - if !has_exp { - return Err(ParseError("Exponent has no digits")); - } - - if exp_minus { - r.exp = -r.exp; - } - - break; - } else { - return Err(ParseError("Invalid character in significand")); - } - } - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - // Hex floats require an exponent but not a hexadecimal point. - if !has_exp { - return Err(ParseError("Hex strings require an exponent")); - } - - // Ignore the exponent if we are zero. - let first_sig_digit = match first_sig_digit { - Some(p) => p, - None => return Ok(Status::OK.and(Self::ZERO)), - }; - - // Calculate the exponent adjustment implicit in the number of - // significant digits and adjust for writing the significand starting - // at the most significant nibble. - let exp_adjustment = if dot > first_sig_digit { - ExpInt::try_from(dot - first_sig_digit).unwrap() - } else { - -ExpInt::try_from(first_sig_digit - dot - 1).unwrap() - }; - let exp_adjustment = exp_adjustment - .saturating_mul(4) - .saturating_sub(1) - .saturating_add(S::PRECISION as ExpInt) - .saturating_sub(LIMB_BITS as ExpInt); - r.exp = r.exp.saturating_add(exp_adjustment); - - Ok(r.normalize(round, loss.unwrap_or(Loss::ExactlyZero))) - } - - fn from_decimal_string(s: &str, round: Round) -> Result, ParseError> { - // Given a normal decimal floating point number of the form - // - // dddd.dddd[eE][+-]ddd - // - // where the decimal point and exponent are optional, fill out the - // variables below. Exponent is appropriate if the significand is - // treated as an integer, and normalized_exp if the significand - // is taken to have the decimal point after a single leading - // non-zero digit. - // - // If the value is zero, first_sig_digit is None. - - let mut any_digits = false; - let mut dec_exp = 0i32; - - // Without leading or trailing zeros, irrespective of the dot. - let mut first_sig_digit = None; - let mut last_sig_digit = 0; - let mut dot = s.len(); - - for (p, c) in s.char_indices() { - if c == '.' { - if dot != s.len() { - return Err(ParseError("String contains multiple dots")); - } - dot = p; - } else if let Some(dec_value) = c.to_digit(10) { - any_digits = true; - - if dec_value != 0 { - if first_sig_digit.is_none() { - first_sig_digit = Some(p); - } - last_sig_digit = p; - } - } else if c == 'e' || c == 'E' { - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - if dot == s.len() { - dot = p; - } - - let mut chars = s[p + 1..].chars().peekable(); - - // Adjust for the given exponent. - let exp_minus = chars.peek() == Some(&'-'); - if exp_minus || chars.peek() == Some(&'+') { - chars.next(); - } - - any_digits = false; - for c in chars { - if let Some(value) = c.to_digit(10) { - any_digits = true; - dec_exp = dec_exp.saturating_mul(10).saturating_add(value as i32); - } else { - return Err(ParseError("Invalid character in exponent")); - } - } - if !any_digits { - return Err(ParseError("Exponent has no digits")); - } - - if exp_minus { - dec_exp = -dec_exp; - } - - break; - } else { - return Err(ParseError("Invalid character in significand")); - } - } - if !any_digits { - return Err(ParseError("Significand has no digits")); - } - - // Test if we have a zero number allowing for non-zero exponents. - let first_sig_digit = match first_sig_digit { - Some(p) => p, - None => return Ok(Status::OK.and(Self::ZERO)), - }; - - // Adjust the exponents for any decimal point. - if dot > last_sig_digit { - dec_exp = dec_exp.saturating_add((dot - last_sig_digit - 1) as i32); - } else { - dec_exp = dec_exp.saturating_sub((last_sig_digit - dot) as i32); - } - let significand_digits = last_sig_digit - first_sig_digit + 1 - - (dot > first_sig_digit && dot < last_sig_digit) as usize; - let normalized_exp = dec_exp.saturating_add(significand_digits as i32 - 1); - - // Handle the cases where exponents are obviously too large or too - // small. Writing L for log 10 / log 2, a number d.ddddd*10^dec_exp - // definitely overflows if - // - // (dec_exp - 1) * L >= MAX_EXP - // - // and definitely underflows to zero where - // - // (dec_exp + 1) * L <= MIN_EXP - PRECISION - // - // With integer arithmetic the tightest bounds for L are - // - // 93/28 < L < 196/59 [ numerator <= 256 ] - // 42039/12655 < L < 28738/8651 [ numerator <= 65536 ] - - // Check for MAX_EXP. - if normalized_exp.saturating_sub(1).saturating_mul(42039) >= 12655 * S::MAX_EXP as i32 { - // Overflow and round. - return Ok(Self::overflow_result(round)); - } - - // Check for MIN_EXP. - if normalized_exp.saturating_add(1).saturating_mul(28738) - <= 8651 * (S::MIN_EXP as i32 - S::PRECISION as i32) - { - // Underflow to zero and round. - let r = - if round == Round::TowardPositive { IeeeFloat::SMALLEST } else { IeeeFloat::ZERO }; - return Ok((Status::UNDERFLOW | Status::INEXACT).and(r)); - } - - // A tight upper bound on number of bits required to hold an - // N-digit decimal integer is N * 196 / 59. Allocate enough space - // to hold the full significand, and an extra limb required by - // tcMultiplyPart. - let max_limbs = limbs_for_bits(1 + 196 * significand_digits / 59); - let mut dec_sig: SmallVec<[Limb; 1]> = SmallVec::with_capacity(max_limbs); - - // Convert to binary efficiently - we do almost all multiplication - // in a Limb. When this would overflow do we do a single - // bignum multiplication, and then revert again to multiplication - // in a Limb. - let mut chars = s[first_sig_digit..=last_sig_digit].chars(); - loop { - let mut val = 0; - let mut multiplier = 1; - - loop { - let dec_value = match chars.next() { - Some('.') => continue, - Some(c) => c.to_digit(10).unwrap(), - None => break, - }; - - multiplier *= 10; - val = val * 10 + dec_value as Limb; - - // The maximum number that can be multiplied by ten with any - // digit added without overflowing a Limb. - if multiplier > (!0 - 9) / 10 { - break; - } - } - - // If we've consumed no digits, we're done. - if multiplier == 1 { - break; - } - - // Multiply out the current limb. - let mut carry = val; - for x in &mut dec_sig { - let [low, mut high] = sig::widening_mul(*x, multiplier); - - // Now add carry. - let (low, overflow) = low.overflowing_add(carry); - high += overflow as Limb; - - *x = low; - carry = high; - } - - // If we had carry, we need another limb (likely but not guaranteed). - if carry > 0 { - dec_sig.push(carry); - } - } - - // Calculate pow(5, abs(dec_exp)) into `pow5_full`. - // The *_calc Vec's are reused scratch space, as an optimization. - let (pow5_full, mut pow5_calc, mut sig_calc, mut sig_scratch_calc) = { - let mut power = dec_exp.abs() as usize; - - const FIRST_EIGHT_POWERS: [Limb; 8] = [1, 5, 25, 125, 625, 3125, 15625, 78125]; - - let mut p5_scratch = smallvec![]; - let mut p5: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[4]]; - - let mut r_scratch = smallvec![]; - let mut r: SmallVec<[Limb; 1]> = smallvec![FIRST_EIGHT_POWERS[power & 7]]; - power >>= 3; - - while power > 0 { - // Calculate pow(5,pow(2,n+3)). - p5_scratch.resize(p5.len() * 2, 0); - let _: Loss = sig::mul(&mut p5_scratch, &mut 0, &p5, &p5, p5.len() * 2 * LIMB_BITS); - while p5_scratch.last() == Some(&0) { - p5_scratch.pop(); - } - mem::swap(&mut p5, &mut p5_scratch); - - if power & 1 != 0 { - r_scratch.resize(r.len() + p5.len(), 0); - let _: Loss = - sig::mul(&mut r_scratch, &mut 0, &r, &p5, (r.len() + p5.len()) * LIMB_BITS); - while r_scratch.last() == Some(&0) { - r_scratch.pop(); - } - mem::swap(&mut r, &mut r_scratch); - } - - power >>= 1; - } - - (r, r_scratch, p5, p5_scratch) - }; - - // Attempt dec_sig * 10^dec_exp with increasing precision. - let mut attempt = 0; - loop { - let calc_precision = (LIMB_BITS << attempt) - 1; - attempt += 1; - - let calc_normal_from_limbs = |sig: &mut SmallVec<[Limb; 1]>, - limbs: &[Limb]| - -> StatusAnd { - sig.resize(limbs_for_bits(calc_precision), 0); - let (mut loss, mut exp) = sig::from_limbs(sig, limbs, calc_precision); - - // Before rounding normalize the exponent of Category::Normal numbers. - let mut omsb = sig::omsb(sig); - - assert_ne!(omsb, 0); - - // OMSB is numbered from 1. We want to place it in the integer - // bit numbered PRECISION if possible, with a compensating change in - // the exponent. - let final_exp = exp.saturating_add(omsb as ExpInt - calc_precision as ExpInt); - - // Shifting left is easy as we don't lose precision. - if final_exp < exp { - assert_eq!(loss, Loss::ExactlyZero); - - let exp_change = (exp - final_exp) as usize; - sig::shift_left(sig, &mut exp, exp_change); - - return Status::OK.and(exp); - } - - // Shift right and capture any new lost fraction. - if final_exp > exp { - let exp_change = (final_exp - exp) as usize; - loss = sig::shift_right(sig, &mut exp, exp_change).combine(loss); - - // Keep OMSB up-to-date. - omsb = omsb.saturating_sub(exp_change); - } - - assert_eq!(omsb, calc_precision); - - // Now round the number according to round given the lost - // fraction. - - // As specified in IEEE 754, since we do not trap we do not report - // underflow for exact results. - if loss == Loss::ExactlyZero { - return Status::OK.and(exp); - } - - // Increment the significand if we're rounding away from zero. - if loss == Loss::MoreThanHalf || loss == Loss::ExactlyHalf && sig::get_bit(sig, 0) { - // We should never overflow. - assert_eq!(sig::increment(sig), 0); - omsb = sig::omsb(sig); - - // Did the significand increment overflow? - if omsb == calc_precision + 1 { - let _: Loss = sig::shift_right(sig, &mut exp, 1); - - return Status::INEXACT.and(exp); - } - } - - // The normal case - we were and are not denormal, and any - // significand increment above didn't overflow. - Status::INEXACT.and(exp) - }; - - let status; - let mut exp = unpack!(status=, - calc_normal_from_limbs(&mut sig_calc, &dec_sig)); - let pow5_status; - let pow5_exp = unpack!(pow5_status=, - calc_normal_from_limbs(&mut pow5_calc, &pow5_full)); - - // Add dec_exp, as 10^n = 5^n * 2^n. - exp += dec_exp as ExpInt; - - let mut used_bits = S::PRECISION; - let mut truncated_bits = calc_precision - used_bits; - - let half_ulp_err1 = (status != Status::OK) as Limb; - let (calc_loss, half_ulp_err2); - if dec_exp >= 0 { - exp += pow5_exp; - - sig_scratch_calc.resize(sig_calc.len() + pow5_calc.len(), 0); - calc_loss = sig::mul( - &mut sig_scratch_calc, - &mut exp, - &sig_calc, - &pow5_calc, - calc_precision, - ); - mem::swap(&mut sig_calc, &mut sig_scratch_calc); - - half_ulp_err2 = (pow5_status != Status::OK) as Limb; - } else { - exp -= pow5_exp; - - sig_scratch_calc.resize(sig_calc.len(), 0); - calc_loss = sig::div( - &mut sig_scratch_calc, - &mut exp, - &mut sig_calc, - &mut pow5_calc, - calc_precision, - ); - mem::swap(&mut sig_calc, &mut sig_scratch_calc); - - // Denormal numbers have less precision. - if exp < S::MIN_EXP { - truncated_bits += (S::MIN_EXP - exp) as usize; - used_bits = calc_precision.saturating_sub(truncated_bits); - } - // Extra half-ulp lost in reciprocal of exponent. - half_ulp_err2 = - 2 * (pow5_status != Status::OK || calc_loss != Loss::ExactlyZero) as Limb; - } - - // Both sig::mul and sig::div return the - // result with the integer bit set. - assert!(sig::get_bit(&sig_calc, calc_precision - 1)); - - // The error from the true value, in half-ulps, on multiplying two - // floating point numbers, which differ from the value they - // approximate by at most half_ulp_err1 and half_ulp_err2 half-ulps, is strictly less - // than the returned value. - // - // See "How to Read Floating Point Numbers Accurately" by William D Clinger. - assert!(half_ulp_err1 < 2 || half_ulp_err2 < 2 || (half_ulp_err1 + half_ulp_err2 < 8)); - - let inexact = (calc_loss != Loss::ExactlyZero) as Limb; - let half_ulp_err = if half_ulp_err1 + half_ulp_err2 == 0 { - inexact * 2 // <= inexact half-ulps. - } else { - inexact + 2 * (half_ulp_err1 + half_ulp_err2) - }; - - let ulps_from_boundary = { - let bits = calc_precision - used_bits - 1; - - let i = bits / LIMB_BITS; - let limb = sig_calc[i] & (!0 >> (LIMB_BITS - 1 - bits % LIMB_BITS)); - let boundary = match round { - Round::NearestTiesToEven | Round::NearestTiesToAway => 1 << (bits % LIMB_BITS), - _ => 0, - }; - if i == 0 { - let delta = limb.wrapping_sub(boundary); - cmp::min(delta, delta.wrapping_neg()) - } else if limb == boundary { - if !sig::is_all_zeros(&sig_calc[1..i]) { - !0 // A lot. - } else { - sig_calc[0] - } - } else if limb == boundary.wrapping_sub(1) { - if sig_calc[1..i].iter().any(|&x| x.wrapping_neg() != 1) { - !0 // A lot. - } else { - sig_calc[0].wrapping_neg() - } - } else { - !0 // A lot. - } - }; - - // Are we guaranteed to round correctly if we truncate? - if ulps_from_boundary.saturating_mul(2) >= half_ulp_err { - let mut r = IeeeFloat { - sig: [0], - exp, - category: Category::Normal, - sign: false, - marker: PhantomData, - }; - sig::extract(&mut r.sig, &sig_calc, used_bits, calc_precision - used_bits); - // If we extracted less bits above we must adjust our exponent - // to compensate for the implicit right shift. - r.exp += (S::PRECISION - used_bits) as ExpInt; - let loss = Loss::through_truncation(&sig_calc, truncated_bits); - return Ok(r.normalize(round, loss)); - } - } - } -} - -impl Loss { - /// Combine the effect of two lost fractions. - fn combine(self, less_significant: Loss) -> Loss { - let mut more_significant = self; - if less_significant != Loss::ExactlyZero { - if more_significant == Loss::ExactlyZero { - more_significant = Loss::LessThanHalf; - } else if more_significant == Loss::ExactlyHalf { - more_significant = Loss::MoreThanHalf; - } - } - - more_significant - } - - /// Returns the fraction lost were a bignum truncated losing the least - /// significant `bits` bits. - fn through_truncation(limbs: &[Limb], bits: usize) -> Loss { - if bits == 0 { - return Loss::ExactlyZero; - } - - let half_bit = bits - 1; - let half_limb = half_bit / LIMB_BITS; - let (half_limb, rest) = if half_limb < limbs.len() { - (limbs[half_limb], &limbs[..half_limb]) - } else { - (0, limbs) - }; - let half = 1 << (half_bit % LIMB_BITS); - let has_half = half_limb & half != 0; - let has_rest = half_limb & (half - 1) != 0 || !sig::is_all_zeros(rest); - - match (has_half, has_rest) { - (false, false) => Loss::ExactlyZero, - (false, true) => Loss::LessThanHalf, - (true, false) => Loss::ExactlyHalf, - (true, true) => Loss::MoreThanHalf, - } - } -} - -/// Implementation details of IeeeFloat significands, such as big integer arithmetic. -/// As a rule of thumb, no functions in this module should dynamically allocate. -mod sig { - use super::{limbs_for_bits, ExpInt, Limb, Loss, LIMB_BITS}; - use core::cmp::Ordering; - use core::iter; - use core::mem; - - pub(super) fn is_all_zeros(limbs: &[Limb]) -> bool { - limbs.iter().all(|&l| l == 0) - } - - /// One, not zero, based LSB. That is, returns 0 for a zeroed significand. - pub(super) fn olsb(limbs: &[Limb]) -> usize { - limbs - .iter() - .enumerate() - .find(|(_, &limb)| limb != 0) - .map_or(0, |(i, limb)| i * LIMB_BITS + limb.trailing_zeros() as usize + 1) - } - - /// One, not zero, based MSB. That is, returns 0 for a zeroed significand. - pub(super) fn omsb(limbs: &[Limb]) -> usize { - limbs - .iter() - .enumerate() - .rfind(|(_, &limb)| limb != 0) - .map_or(0, |(i, limb)| (i + 1) * LIMB_BITS - limb.leading_zeros() as usize) - } - - /// Comparison (unsigned) of two significands. - pub(super) fn cmp(a: &[Limb], b: &[Limb]) -> Ordering { - assert_eq!(a.len(), b.len()); - for (a, b) in a.iter().zip(b).rev() { - match a.cmp(b) { - Ordering::Equal => {} - o => return o, - } - } - - Ordering::Equal - } - - /// Extracts the given bit. - pub(super) fn get_bit(limbs: &[Limb], bit: usize) -> bool { - limbs[bit / LIMB_BITS] & (1 << (bit % LIMB_BITS)) != 0 - } - - /// Sets the given bit. - pub(super) fn set_bit(limbs: &mut [Limb], bit: usize) { - limbs[bit / LIMB_BITS] |= 1 << (bit % LIMB_BITS); - } - - /// Clear the given bit. - pub(super) fn clear_bit(limbs: &mut [Limb], bit: usize) { - limbs[bit / LIMB_BITS] &= !(1 << (bit % LIMB_BITS)); - } - - /// Shifts `dst` left `bits` bits, subtract `bits` from its exponent. - pub(super) fn shift_left(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) { - if bits > 0 { - // Our exponent should not underflow. - *exp = exp.checked_sub(bits as ExpInt).unwrap(); - - // Jump is the inter-limb jump; shift is the intra-limb shift. - let jump = bits / LIMB_BITS; - let shift = bits % LIMB_BITS; - - for i in (0..dst.len()).rev() { - let mut limb; - - if i < jump { - limb = 0; - } else { - // dst[i] comes from the two limbs src[i - jump] and, if we have - // an intra-limb shift, src[i - jump - 1]. - limb = dst[i - jump]; - if shift > 0 { - limb <<= shift; - if i > jump { - limb |= dst[i - jump - 1] >> (LIMB_BITS - shift); - } - } - } - - dst[i] = limb; - } - } - } - - /// Shifts `dst` right `bits` bits noting lost fraction. - pub(super) fn shift_right(dst: &mut [Limb], exp: &mut ExpInt, bits: usize) -> Loss { - let loss = Loss::through_truncation(dst, bits); - - if bits > 0 { - // Our exponent should not overflow. - *exp = exp.checked_add(bits as ExpInt).unwrap(); - - // Jump is the inter-limb jump; shift is the intra-limb shift. - let jump = bits / LIMB_BITS; - let shift = bits % LIMB_BITS; - - // Perform the shift. This leaves the most significant `bits` bits - // of the result at zero. - for i in 0..dst.len() { - let mut limb; - - if i + jump >= dst.len() { - limb = 0; - } else { - limb = dst[i + jump]; - if shift > 0 { - limb >>= shift; - if i + jump + 1 < dst.len() { - limb |= dst[i + jump + 1] << (LIMB_BITS - shift); - } - } - } - - dst[i] = limb; - } - } - - loss - } - - /// Copies the bit vector of width `src_bits` from `src`, starting at bit SRC_LSB, - /// to `dst`, such that the bit SRC_LSB becomes the least significant bit of `dst`. - /// All high bits above `src_bits` in `dst` are zero-filled. - pub(super) fn extract(dst: &mut [Limb], src: &[Limb], src_bits: usize, src_lsb: usize) { - if src_bits == 0 { - return; - } - - let dst_limbs = limbs_for_bits(src_bits); - assert!(dst_limbs <= dst.len()); - - let src = &src[src_lsb / LIMB_BITS..]; - dst[..dst_limbs].copy_from_slice(&src[..dst_limbs]); - - let shift = src_lsb % LIMB_BITS; - let _: Loss = shift_right(&mut dst[..dst_limbs], &mut 0, shift); - - // We now have (dst_limbs * LIMB_BITS - shift) bits from `src` - // in `dst`. If this is less that src_bits, append the rest, else - // clear the high bits. - let n = dst_limbs * LIMB_BITS - shift; - if n < src_bits { - let mask = (1 << (src_bits - n)) - 1; - dst[dst_limbs - 1] |= (src[dst_limbs] & mask) << (n % LIMB_BITS); - } else if n > src_bits && src_bits % LIMB_BITS > 0 { - dst[dst_limbs - 1] &= (1 << (src_bits % LIMB_BITS)) - 1; - } - - // Clear high limbs. - for x in &mut dst[dst_limbs..] { - *x = 0; - } - } - - /// We want the most significant PRECISION bits of `src`. There may not - /// be that many; extract what we can. - pub(super) fn from_limbs(dst: &mut [Limb], src: &[Limb], precision: usize) -> (Loss, ExpInt) { - let omsb = omsb(src); - - if precision <= omsb { - extract(dst, src, precision, omsb - precision); - (Loss::through_truncation(src, omsb - precision), omsb as ExpInt - 1) - } else { - extract(dst, src, omsb, 0); - (Loss::ExactlyZero, precision as ExpInt - 1) - } - } - - /// For every consecutive chunk of `bits` bits from `limbs`, - /// going from most significant to the least significant bits, - /// call `f` to transform those bits and store the result back. - pub(super) fn each_chunk Limb>(limbs: &mut [Limb], bits: usize, mut f: F) { - assert_eq!(LIMB_BITS % bits, 0); - for limb in limbs.iter_mut().rev() { - let mut r = 0; - for i in (0..LIMB_BITS / bits).rev() { - r |= f((*limb >> (i * bits)) & ((1 << bits) - 1)) << (i * bits); - } - *limb = r; - } - } - - /// Increment in-place, return the carry flag. - pub(super) fn increment(dst: &mut [Limb]) -> Limb { - for x in dst { - *x = x.wrapping_add(1); - if *x != 0 { - return 0; - } - } - - 1 - } - - /// Decrement in-place, return the borrow flag. - pub(super) fn decrement(dst: &mut [Limb]) -> Limb { - for x in dst { - *x = x.wrapping_sub(1); - if *x != !0 { - return 0; - } - } - - 1 - } - - /// `a += b + c` where `c` is zero or one. Returns the carry flag. - pub(super) fn add(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb { - assert!(c <= 1); - - for (a, &b) in iter::zip(a, b) { - let (r, overflow) = a.overflowing_add(b); - let (r, overflow2) = r.overflowing_add(c); - *a = r; - c = (overflow | overflow2) as Limb; - } - - c - } - - /// `a -= b + c` where `c` is zero or one. Returns the borrow flag. - pub(super) fn sub(a: &mut [Limb], b: &[Limb], mut c: Limb) -> Limb { - assert!(c <= 1); - - for (a, &b) in iter::zip(a, b) { - let (r, overflow) = a.overflowing_sub(b); - let (r, overflow2) = r.overflowing_sub(c); - *a = r; - c = (overflow | overflow2) as Limb; - } - - c - } - - /// `a += b` or `a -= b`. Does not preserve `b`. - pub(super) fn add_or_sub( - a_sig: &mut [Limb], - a_exp: &mut ExpInt, - a_sign: &mut bool, - b_sig: &mut [Limb], - b_exp: ExpInt, - b_sign: bool, - ) -> Loss { - // Are we bigger exponent-wise than the RHS? - let bits = *a_exp - b_exp; - - // Determine if the operation on the absolute values is effectively - // an addition or subtraction. - // Subtraction is more subtle than one might naively expect. - if *a_sign ^ b_sign { - let (reverse, loss); - - if bits == 0 { - reverse = cmp(a_sig, b_sig) == Ordering::Less; - loss = Loss::ExactlyZero; - } else if bits > 0 { - loss = shift_right(b_sig, &mut 0, (bits - 1) as usize); - shift_left(a_sig, a_exp, 1); - reverse = false; - } else { - loss = shift_right(a_sig, a_exp, (-bits - 1) as usize); - shift_left(b_sig, &mut 0, 1); - reverse = true; - } - - let borrow = (loss != Loss::ExactlyZero) as Limb; - if reverse { - // The code above is intended to ensure that no borrow is necessary. - assert_eq!(sub(b_sig, a_sig, borrow), 0); - a_sig.copy_from_slice(b_sig); - *a_sign = !*a_sign; - } else { - // The code above is intended to ensure that no borrow is necessary. - assert_eq!(sub(a_sig, b_sig, borrow), 0); - } - - // Invert the lost fraction - it was on the RHS and subtracted. - match loss { - Loss::LessThanHalf => Loss::MoreThanHalf, - Loss::MoreThanHalf => Loss::LessThanHalf, - _ => loss, - } - } else { - let loss = if bits > 0 { - shift_right(b_sig, &mut 0, bits as usize) - } else { - shift_right(a_sig, a_exp, -bits as usize) - }; - // We have a guard bit; generating a carry cannot happen. - assert_eq!(add(a_sig, b_sig, 0), 0); - loss - } - } - - /// `[low, high] = a * b`. - /// - /// This cannot overflow, because - /// - /// `(n - 1) * (n - 1) + 2 * (n - 1) == (n - 1) * (n + 1)` - /// - /// which is less than n2. - pub(super) fn widening_mul(a: Limb, b: Limb) -> [Limb; 2] { - let mut wide = [0, 0]; - - if a == 0 || b == 0 { - return wide; - } - - const HALF_BITS: usize = LIMB_BITS / 2; - - let select = |limb, i| (limb >> (i * HALF_BITS)) & ((1 << HALF_BITS) - 1); - for i in 0..2 { - for j in 0..2 { - let mut x = [select(a, i) * select(b, j), 0]; - shift_left(&mut x, &mut 0, (i + j) * HALF_BITS); - assert_eq!(add(&mut wide, &x, 0), 0); - } - } - - wide - } - - /// `dst = a * b` (for normal `a` and `b`). Returns the lost fraction. - pub(super) fn mul<'a>( - dst: &mut [Limb], - exp: &mut ExpInt, - mut a: &'a [Limb], - mut b: &'a [Limb], - precision: usize, - ) -> Loss { - // Put the narrower number on the `a` for less loops below. - if a.len() > b.len() { - mem::swap(&mut a, &mut b); - } - - for x in &mut dst[..b.len()] { - *x = 0; - } - - for i in 0..a.len() { - let mut carry = 0; - for j in 0..b.len() { - let [low, mut high] = widening_mul(a[i], b[j]); - - // Now add carry. - let (low, overflow) = low.overflowing_add(carry); - high += overflow as Limb; - - // And now `dst[i + j]`, and store the new low part there. - let (low, overflow) = low.overflowing_add(dst[i + j]); - high += overflow as Limb; - - dst[i + j] = low; - carry = high; - } - dst[i + b.len()] = carry; - } - - // Assume the operands involved in the multiplication are single-precision - // FP, and the two multiplicants are: - // a = a23 . a22 ... a0 * 2^e1 - // b = b23 . b22 ... b0 * 2^e2 - // the result of multiplication is: - // dst = c48 c47 c46 . c45 ... c0 * 2^(e1+e2) - // Note that there are three significant bits at the left-hand side of the - // radix point: two for the multiplication, and an overflow bit for the - // addition (that will always be zero at this point). Move the radix point - // toward left by two bits, and adjust exponent accordingly. - *exp += 2; - - // Convert the result having "2 * precision" significant-bits back to the one - // having "precision" significant-bits. First, move the radix point from - // poision "2*precision - 1" to "precision - 1". The exponent need to be - // adjusted by "2*precision - 1" - "precision - 1" = "precision". - *exp -= precision as ExpInt + 1; - - // In case MSB resides at the left-hand side of radix point, shift the - // mantissa right by some amount to make sure the MSB reside right before - // the radix point (i.e., "MSB . rest-significant-bits"). - // - // Note that the result is not normalized when "omsb < precision". So, the - // caller needs to call IeeeFloat::normalize() if normalized value is - // expected. - let omsb = omsb(dst); - if omsb <= precision { Loss::ExactlyZero } else { shift_right(dst, exp, omsb - precision) } - } - - /// `quotient = dividend / divisor`. Returns the lost fraction. - /// Does not preserve `dividend` or `divisor`. - pub(super) fn div( - quotient: &mut [Limb], - exp: &mut ExpInt, - dividend: &mut [Limb], - divisor: &mut [Limb], - precision: usize, - ) -> Loss { - // Normalize the divisor. - let bits = precision - omsb(divisor); - shift_left(divisor, &mut 0, bits); - *exp += bits as ExpInt; - - // Normalize the dividend. - let bits = precision - omsb(dividend); - shift_left(dividend, exp, bits); - - // Division by 1. - let olsb_divisor = olsb(divisor); - if olsb_divisor == precision { - quotient.copy_from_slice(dividend); - return Loss::ExactlyZero; - } - - // Ensure the dividend >= divisor initially for the loop below. - // Incidentally, this means that the division loop below is - // guaranteed to set the integer bit to one. - if cmp(dividend, divisor) == Ordering::Less { - shift_left(dividend, exp, 1); - assert_ne!(cmp(dividend, divisor), Ordering::Less) - } - - // Helper for figuring out the lost fraction. - let lost_fraction = |dividend: &[Limb], divisor: &[Limb]| match cmp(dividend, divisor) { - Ordering::Greater => Loss::MoreThanHalf, - Ordering::Equal => Loss::ExactlyHalf, - Ordering::Less => { - if is_all_zeros(dividend) { - Loss::ExactlyZero - } else { - Loss::LessThanHalf - } - } - }; - - // Try to perform a (much faster) short division for small divisors. - let divisor_bits = precision - (olsb_divisor - 1); - macro_rules! try_short_div { - ($W:ty, $H:ty, $half:expr) => { - if divisor_bits * 2 <= $half { - // Extract the small divisor. - let _: Loss = shift_right(divisor, &mut 0, olsb_divisor - 1); - let divisor = divisor[0] as $H as $W; - - // Shift the dividend to produce a quotient with the unit bit set. - let top_limb = *dividend.last().unwrap(); - let mut rem = (top_limb >> (LIMB_BITS - (divisor_bits - 1))) as $H; - shift_left(dividend, &mut 0, divisor_bits - 1); - - // Apply short division in place on $H (of $half bits) chunks. - each_chunk(dividend, $half, |chunk| { - let chunk = chunk as $H; - let combined = ((rem as $W) << $half) | (chunk as $W); - rem = (combined % divisor) as $H; - (combined / divisor) as $H as Limb - }); - quotient.copy_from_slice(dividend); - - return lost_fraction(&[(rem as Limb) << 1], &[divisor as Limb]); - } - }; - } - - try_short_div!(u32, u16, 16); - try_short_div!(u64, u32, 32); - try_short_div!(u128, u64, 64); - - // Zero the quotient before setting bits in it. - for x in &mut quotient[..limbs_for_bits(precision)] { - *x = 0; - } - - // Long division. - for bit in (0..precision).rev() { - if cmp(dividend, divisor) != Ordering::Less { - sub(dividend, divisor, 0); - set_bit(quotient, bit); - } - shift_left(dividend, &mut 0, 1); - } - - lost_fraction(dividend, divisor) - } -} diff --git a/compiler/rustc_apfloat/src/lib.rs b/compiler/rustc_apfloat/src/lib.rs deleted file mode 100644 index dde368e7b924..000000000000 --- a/compiler/rustc_apfloat/src/lib.rs +++ /dev/null @@ -1,695 +0,0 @@ -//! Port of LLVM's APFloat software floating-point implementation from the -//! following C++ sources (please update commit hash when backporting): -//! -//! -//! * `include/llvm/ADT/APFloat.h` -> `Float` and `FloatConvert` traits -//! * `lib/Support/APFloat.cpp` -> `ieee` and `ppc` modules -//! * `unittests/ADT/APFloatTest.cpp` -> `tests` directory -//! -//! The port contains no unsafe code, global state, or side-effects in general, -//! and the only allocations are in the conversion to/from decimal strings. -//! -//! Most of the API and the testcases are intact in some form or another, -//! with some ergonomic changes, such as idiomatic short names, returning -//! new values instead of mutating the receiver, and having separate method -//! variants that take a non-default rounding mode (with the suffix `_r`). -//! Comments have been preserved where possible, only slightly adapted. -//! -//! Instead of keeping a pointer to a configuration struct and inspecting it -//! dynamically on every operation, types (e.g., `ieee::Double`), traits -//! (e.g., `ieee::Semantics`) and associated constants are employed for -//! increased type safety and performance. -//! -//! On-heap bigints are replaced everywhere (except in decimal conversion), -//! with short arrays of `type Limb = u128` elements (instead of `u64`), -//! This allows fitting the largest supported significands in one integer -//! (`ieee::Quad` and `ppc::Fallback` use slightly less than 128 bits). -//! All of the functions in the `ieee::sig` module operate on slices. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![no_std] -#![forbid(unsafe_code)] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] - -#[macro_use] -extern crate alloc; - -use core::cmp::Ordering; -use core::fmt; -use core::ops::{Add, Div, Mul, Neg, Rem, Sub}; -use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; -use core::str::FromStr; - -bitflags::bitflags! { - /// IEEE-754R 7: Default exception handling. - /// - /// UNDERFLOW or OVERFLOW are always returned or-ed with INEXACT. - #[must_use] - pub struct Status: u8 { - const OK = 0x00; - const INVALID_OP = 0x01; - const DIV_BY_ZERO = 0x02; - const OVERFLOW = 0x04; - const UNDERFLOW = 0x08; - const INEXACT = 0x10; - } -} - -#[must_use] -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct StatusAnd { - pub status: Status, - pub value: T, -} - -impl Status { - pub fn and(self, value: T) -> StatusAnd { - StatusAnd { status: self, value } - } -} - -impl StatusAnd { - pub fn map U, U>(self, f: F) -> StatusAnd { - StatusAnd { status: self.status, value: f(self.value) } - } -} - -#[macro_export] -macro_rules! unpack { - ($status:ident|=, $e:expr) => { - match $e { - $crate::StatusAnd { status, value } => { - $status |= status; - value - } - } - }; - ($status:ident=, $e:expr) => { - match $e { - $crate::StatusAnd { status, value } => { - $status = status; - value - } - } - }; -} - -/// Category of internally-represented number. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Category { - Infinity, - NaN, - Normal, - Zero, -} - -/// IEEE-754R 4.3: Rounding-direction attributes. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Round { - NearestTiesToEven, - TowardPositive, - TowardNegative, - TowardZero, - NearestTiesToAway, -} - -impl Neg for Round { - type Output = Round; - fn neg(self) -> Round { - match self { - Round::TowardPositive => Round::TowardNegative, - Round::TowardNegative => Round::TowardPositive, - Round::NearestTiesToEven | Round::TowardZero | Round::NearestTiesToAway => self, - } - } -} - -/// A signed type to represent a floating point number's unbiased exponent. -pub type ExpInt = i16; - -// \c ilogb error results. -pub const IEK_INF: ExpInt = ExpInt::MAX; -pub const IEK_NAN: ExpInt = ExpInt::MIN; -pub const IEK_ZERO: ExpInt = ExpInt::MIN + 1; - -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct ParseError(pub &'static str); - -/// A self-contained host- and target-independent arbitrary-precision -/// floating-point software implementation. -/// -/// `apfloat` uses significand bignum integer arithmetic as provided by functions -/// in the `ieee::sig`. -/// -/// Written for clarity rather than speed, in particular with a view to use in -/// the front-end of a cross compiler so that target arithmetic can be correctly -/// performed on the host. Performance should nonetheless be reasonable, -/// particularly for its intended use. It may be useful as a base -/// implementation for a run-time library during development of a faster -/// target-specific one. -/// -/// All 5 rounding modes in the IEEE-754R draft are handled correctly for all -/// implemented operations. Currently implemented operations are add, subtract, -/// multiply, divide, fused-multiply-add, conversion-to-float, -/// conversion-to-integer and conversion-from-integer. New rounding modes -/// (e.g., away from zero) can be added with three or four lines of code. -/// -/// Four formats are built-in: IEEE single precision, double precision, -/// quadruple precision, and x87 80-bit extended double (when operating with -/// full extended precision). Adding a new format that obeys IEEE semantics -/// only requires adding two lines of code: a declaration and definition of the -/// format. -/// -/// All operations return the status of that operation as an exception bit-mask, -/// so multiple operations can be done consecutively with their results or-ed -/// together. The returned status can be useful for compiler diagnostics; e.g., -/// inexact, underflow and overflow can be easily diagnosed on constant folding, -/// and compiler optimizers can determine what exceptions would be raised by -/// folding operations and optimize, or perhaps not optimize, accordingly. -/// -/// At present, underflow tininess is detected after rounding; it should be -/// straight forward to add support for the before-rounding case too. -/// -/// The library reads hexadecimal floating point numbers as per C99, and -/// correctly rounds if necessary according to the specified rounding mode. -/// Syntax is required to have been validated by the caller. -/// -/// It also reads decimal floating point numbers and correctly rounds according -/// to the specified rounding mode. -/// -/// Non-zero finite numbers are represented internally as a sign bit, a 16-bit -/// signed exponent, and the significand as an array of integer limbs. After -/// normalization of a number of precision P the exponent is within the range of -/// the format, and if the number is not denormal the P-th bit of the -/// significand is set as an explicit integer bit. For denormals the most -/// significant bit is shifted right so that the exponent is maintained at the -/// format's minimum, so that the smallest denormal has just the least -/// significant bit of the significand set. The sign of zeros and infinities -/// is significant; the exponent and significand of such numbers is not stored, -/// but has a known implicit (deterministic) value: 0 for the significands, 0 -/// for zero exponent, all 1 bits for infinity exponent. For NaNs the sign and -/// significand are deterministic, although not really meaningful, and preserved -/// in non-conversion operations. The exponent is implicitly all 1 bits. -/// -/// `apfloat` does not provide any exception handling beyond default exception -/// handling. We represent Signaling NaNs via IEEE-754R 2008 6.2.1 should clause -/// by encoding Signaling NaNs with the first bit of its trailing significand -/// as 0. -/// -/// Future work -/// =========== -/// -/// Some features that may or may not be worth adding: -/// -/// Optional ability to detect underflow tininess before rounding. -/// -/// New formats: x87 in single and double precision mode (IEEE apart from -/// extended exponent range) (hard). -/// -/// New operations: sqrt, nexttoward. -/// -pub trait Float: - Copy - + Default - + FromStr - + PartialOrd - + fmt::Display - + Neg - + AddAssign - + SubAssign - + MulAssign - + DivAssign - + RemAssign - + Add> - + Sub> - + Mul> - + Div> - + Rem> -{ - /// Total number of bits in the in-memory format. - const BITS: usize; - - /// Number of bits in the significand. This includes the integer bit. - const PRECISION: usize; - - /// The largest E such that 2E is representable; this matches the - /// definition of IEEE 754. - const MAX_EXP: ExpInt; - - /// The smallest E such that 2E is a normalized number; this - /// matches the definition of IEEE 754. - const MIN_EXP: ExpInt; - - /// Positive Zero. - const ZERO: Self; - - /// Positive Infinity. - const INFINITY: Self; - - /// NaN (Not a Number). - // FIXME(eddyb) provide a default when qnan becomes const fn. - const NAN: Self; - - /// Factory for QNaN values. - // FIXME(eddyb) should be const fn. - fn qnan(payload: Option) -> Self; - - /// Factory for SNaN values. - // FIXME(eddyb) should be const fn. - fn snan(payload: Option) -> Self; - - /// Largest finite number. - // FIXME(eddyb) should be const (but FloatPair::largest is nontrivial). - fn largest() -> Self; - - /// Smallest (by magnitude) finite number. - /// Might be denormalized, which implies a relative loss of precision. - const SMALLEST: Self; - - /// Smallest (by magnitude) normalized finite number. - // FIXME(eddyb) should be const (but FloatPair::smallest_normalized is nontrivial). - fn smallest_normalized() -> Self; - - // Arithmetic - - fn add_r(self, rhs: Self, round: Round) -> StatusAnd; - fn sub_r(self, rhs: Self, round: Round) -> StatusAnd { - self.add_r(-rhs, round) - } - fn mul_r(self, rhs: Self, round: Round) -> StatusAnd; - fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd; - fn mul_add(self, multiplicand: Self, addend: Self) -> StatusAnd { - self.mul_add_r(multiplicand, addend, Round::NearestTiesToEven) - } - fn div_r(self, rhs: Self, round: Round) -> StatusAnd; - /// IEEE remainder. - // This is not currently correct in all cases. - fn ieee_rem(self, rhs: Self) -> StatusAnd { - let mut v = self; - - let status; - v = unpack!(status=, v / rhs); - if status == Status::DIV_BY_ZERO { - return status.and(self); - } - - assert!(Self::PRECISION < 128); - - let status; - let x = unpack!(status=, v.to_i128_r(128, Round::NearestTiesToEven, &mut false)); - if status == Status::INVALID_OP { - return status.and(self); - } - - let status; - let mut v = unpack!(status=, Self::from_i128(x)); - assert_eq!(status, Status::OK); // should always work - - let status; - v = unpack!(status=, v * rhs); - assert_eq!(status - Status::INEXACT, Status::OK); // should not overflow or underflow - - let status; - v = unpack!(status=, self - v); - assert_eq!(status - Status::INEXACT, Status::OK); // likewise - - if v.is_zero() { - status.and(v.copy_sign(self)) // IEEE754 requires this - } else { - status.and(v) - } - } - /// C fmod, or llvm frem. - fn c_fmod(self, rhs: Self) -> StatusAnd; - fn round_to_integral(self, round: Round) -> StatusAnd; - - /// IEEE-754R 2008 5.3.1: nextUp. - fn next_up(self) -> StatusAnd; - - /// IEEE-754R 2008 5.3.1: nextDown. - /// - /// *NOTE* since nextDown(x) = -nextUp(-x), we only implement nextUp with - /// appropriate sign switching before/after the computation. - fn next_down(self) -> StatusAnd { - (-self).next_up().map(|r| -r) - } - - fn abs(self) -> Self { - if self.is_negative() { -self } else { self } - } - fn copy_sign(self, rhs: Self) -> Self { - if self.is_negative() != rhs.is_negative() { -self } else { self } - } - - // Conversions - fn from_bits(input: u128) -> Self; - fn from_i128_r(input: i128, round: Round) -> StatusAnd { - if input < 0 { - Self::from_u128_r(input.wrapping_neg() as u128, -round).map(|r| -r) - } else { - Self::from_u128_r(input as u128, round) - } - } - fn from_i128(input: i128) -> StatusAnd { - Self::from_i128_r(input, Round::NearestTiesToEven) - } - fn from_u128_r(input: u128, round: Round) -> StatusAnd; - fn from_u128(input: u128) -> StatusAnd { - Self::from_u128_r(input, Round::NearestTiesToEven) - } - fn from_str_r(s: &str, round: Round) -> Result, ParseError>; - fn to_bits(self) -> u128; - - /// Converts a floating point number to an integer according to the - /// rounding mode. In case of an invalid operation exception, - /// deterministic values are returned, namely zero for NaNs and the - /// minimal or maximal value respectively for underflow or overflow. - /// If the rounded value is in range but the floating point number is - /// not the exact integer, the C standard doesn't require an inexact - /// exception to be raised. IEEE-854 does require it so we do that. - /// - /// Note that for conversions to integer type the C standard requires - /// round-to-zero to always be used. - /// - /// The *is_exact output tells whether the result is exact, in the sense - /// that converting it back to the original floating point type produces - /// the original value. This is almost equivalent to `result == Status::OK`, - /// except for negative zeroes. - fn to_i128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd { - let status; - if self.is_negative() { - if self.is_zero() { - // Negative zero can't be represented as an int. - *is_exact = false; - } - let r = unpack!(status=, (-self).to_u128_r(width, -round, is_exact)); - - // Check for values that don't fit in the signed integer. - if r > (1 << (width - 1)) { - // Return the most negative integer for the given width. - *is_exact = false; - Status::INVALID_OP.and(-1 << (width - 1)) - } else { - status.and(r.wrapping_neg() as i128) - } - } else { - // Positive case is simpler, can pretend it's a smaller unsigned - // integer, and `to_u128` will take care of all the edge cases. - self.to_u128_r(width - 1, round, is_exact).map(|r| r as i128) - } - } - fn to_i128(self, width: usize) -> StatusAnd { - self.to_i128_r(width, Round::TowardZero, &mut true) - } - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd; - fn to_u128(self, width: usize) -> StatusAnd { - self.to_u128_r(width, Round::TowardZero, &mut true) - } - - fn cmp_abs_normal(self, rhs: Self) -> Ordering; - - /// Bitwise comparison for equality (QNaNs compare equal, 0!=-0). - fn bitwise_eq(self, rhs: Self) -> bool; - - // IEEE-754R 5.7.2 General operations. - - /// Implements IEEE minNum semantics. Returns the smaller of the 2 arguments if - /// both are not NaN. If either argument is a NaN, returns the other argument. - fn min(self, other: Self) -> Self { - if self.is_nan() { - other - } else if other.is_nan() { - self - } else if other.partial_cmp(&self) == Some(Ordering::Less) { - other - } else { - self - } - } - - /// Implements IEEE maxNum semantics. Returns the larger of the 2 arguments if - /// both are not NaN. If either argument is a NaN, returns the other argument. - fn max(self, other: Self) -> Self { - if self.is_nan() { - other - } else if other.is_nan() { - self - } else if self.partial_cmp(&other) == Some(Ordering::Less) { - other - } else { - self - } - } - - /// IEEE-754R isSignMinus: Returns whether the current value is - /// negative. - /// - /// This applies to zeros and NaNs as well. - fn is_negative(self) -> bool; - - /// IEEE-754R isNormal: Returns whether the current value is normal. - /// - /// This implies that the current value of the float is not zero, subnormal, - /// infinite, or NaN following the definition of normality from IEEE-754R. - fn is_normal(self) -> bool { - !self.is_denormal() && self.is_finite_non_zero() - } - - /// Returns `true` if the current value is zero, subnormal, or - /// normal. - /// - /// This means that the value is not infinite or NaN. - fn is_finite(self) -> bool { - !self.is_nan() && !self.is_infinite() - } - - /// Returns `true` if the float is plus or minus zero. - fn is_zero(self) -> bool { - self.category() == Category::Zero - } - - /// IEEE-754R isSubnormal(): Returns whether the float is a - /// denormal. - fn is_denormal(self) -> bool; - - /// IEEE-754R isInfinite(): Returns whether the float is infinity. - fn is_infinite(self) -> bool { - self.category() == Category::Infinity - } - - /// Returns `true` if the float is a quiet or signaling NaN. - fn is_nan(self) -> bool { - self.category() == Category::NaN - } - - /// Returns `true` if the float is a signaling NaN. - fn is_signaling(self) -> bool; - - // Simple Queries - - fn category(self) -> Category; - fn is_non_zero(self) -> bool { - !self.is_zero() - } - fn is_finite_non_zero(self) -> bool { - self.is_finite() && !self.is_zero() - } - fn is_pos_zero(self) -> bool { - self.is_zero() && !self.is_negative() - } - fn is_neg_zero(self) -> bool { - self.is_zero() && self.is_negative() - } - - /// Returns `true` if the number has the smallest possible non-zero - /// magnitude in the current semantics. - fn is_smallest(self) -> bool { - Self::SMALLEST.copy_sign(self).bitwise_eq(self) - } - - /// Returns `true` if the number has the largest possible finite - /// magnitude in the current semantics. - fn is_largest(self) -> bool { - Self::largest().copy_sign(self).bitwise_eq(self) - } - - /// Returns `true` if the number is an exact integer. - fn is_integer(self) -> bool { - // This could be made more efficient; I'm going for obviously correct. - if !self.is_finite() { - return false; - } - self.round_to_integral(Round::TowardZero).value.bitwise_eq(self) - } - - /// If this value has an exact multiplicative inverse, return it. - fn get_exact_inverse(self) -> Option; - - /// Returns the exponent of the internal representation of the Float. - /// - /// Because the radix of Float is 2, this is equivalent to floor(log2(x)). - /// For special Float values, this returns special error codes: - /// - /// NaN -> \c IEK_NAN - /// 0 -> \c IEK_ZERO - /// Inf -> \c IEK_INF - /// - fn ilogb(self) -> ExpInt; - - /// Returns: self * 2exp for integral exponents. - /// Equivalent to C standard library function `ldexp`. - fn scalbn_r(self, exp: ExpInt, round: Round) -> Self; - fn scalbn(self, exp: ExpInt) -> Self { - self.scalbn_r(exp, Round::NearestTiesToEven) - } - - /// Equivalent to C standard library function with the same name. - /// - /// While the C standard says exp is an unspecified value for infinity and nan, - /// this returns INT_MAX for infinities, and INT_MIN for NaNs (see `ilogb`). - fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self; - fn frexp(self, exp: &mut ExpInt) -> Self { - self.frexp_r(exp, Round::NearestTiesToEven) - } -} - -pub trait FloatConvert: Float { - /// Converts a value of one floating point type to another. - /// The return value corresponds to the IEEE754 exceptions. *loses_info - /// records whether the transformation lost information, i.e., whether - /// converting the result back to the original type will produce the - /// original value (this is almost the same as return `value == Status::OK`, - /// but there are edge cases where this is not so). - fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd; - fn convert(self, loses_info: &mut bool) -> StatusAnd { - self.convert_r(Round::NearestTiesToEven, loses_info) - } -} - -macro_rules! float_common_impls { - ($ty:ident<$t:tt>) => { - impl<$t> Default for $ty<$t> - where - Self: Float, - { - fn default() -> Self { - Self::ZERO - } - } - - impl<$t> ::core::str::FromStr for $ty<$t> - where - Self: Float, - { - type Err = ParseError; - fn from_str(s: &str) -> Result { - Self::from_str_r(s, Round::NearestTiesToEven).map(|x| x.value) - } - } - - // Rounding ties to the nearest even, by default. - - impl<$t> ::core::ops::Add for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn add(self, rhs: Self) -> StatusAnd { - self.add_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Sub for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn sub(self, rhs: Self) -> StatusAnd { - self.sub_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Mul for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn mul(self, rhs: Self) -> StatusAnd { - self.mul_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Div for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn div(self, rhs: Self) -> StatusAnd { - self.div_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Rem for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd; - fn rem(self, rhs: Self) -> StatusAnd { - self.c_fmod(rhs) - } - } - - impl<$t> ::core::ops::AddAssign for $ty<$t> - where - Self: Float, - { - fn add_assign(&mut self, rhs: Self) { - *self = (*self + rhs).value; - } - } - - impl<$t> ::core::ops::SubAssign for $ty<$t> - where - Self: Float, - { - fn sub_assign(&mut self, rhs: Self) { - *self = (*self - rhs).value; - } - } - - impl<$t> ::core::ops::MulAssign for $ty<$t> - where - Self: Float, - { - fn mul_assign(&mut self, rhs: Self) { - *self = (*self * rhs).value; - } - } - - impl<$t> ::core::ops::DivAssign for $ty<$t> - where - Self: Float, - { - fn div_assign(&mut self, rhs: Self) { - *self = (*self / rhs).value; - } - } - - impl<$t> ::core::ops::RemAssign for $ty<$t> - where - Self: Float, - { - fn rem_assign(&mut self, rhs: Self) { - *self = (*self % rhs).value; - } - } - }; -} - -pub mod ieee; -pub mod ppc; diff --git a/compiler/rustc_apfloat/src/ppc.rs b/compiler/rustc_apfloat/src/ppc.rs deleted file mode 100644 index 65a0f66645be..000000000000 --- a/compiler/rustc_apfloat/src/ppc.rs +++ /dev/null @@ -1,434 +0,0 @@ -use crate::ieee; -use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd}; - -use core::cmp::Ordering; -use core::fmt; -use core::ops::Neg; - -#[must_use] -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] -pub struct DoubleFloat(F, F); -pub type DoubleDouble = DoubleFloat; - -// These are legacy semantics for the Fallback, inaccurate implementation of -// IBM double-double, if the accurate DoubleDouble doesn't handle the -// operation. It's equivalent to having an IEEE number with consecutive 106 -// bits of mantissa and 11 bits of exponent. -// -// It's not equivalent to IBM double-double. For example, a legit IBM -// double-double, 1 + epsilon: -// -// 1 + epsilon = 1 + (1 >> 1076) -// -// is not representable by a consecutive 106 bits of mantissa. -// -// Currently, these semantics are used in the following way: -// -// DoubleDouble -> (Double, Double) -> -// DoubleDouble's Fallback -> IEEE operations -// -// FIXME: Implement all operations in DoubleDouble, and delete these -// semantics. -// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds. -pub struct FallbackS(#[allow(unused)] F); -type Fallback = ieee::IeeeFloat>; -impl ieee::Semantics for FallbackS { - // Forbid any conversion to/from bits. - const BITS: usize = 0; - const PRECISION: usize = F::PRECISION * 2; - const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt; - const MIN_EXP: ExpInt = F::MIN_EXP as ExpInt + F::PRECISION as ExpInt; -} - -// Convert number to F. To avoid spurious underflows, we re- -// normalize against the F exponent range first, and only *then* -// truncate the mantissa. The result of that second conversion -// may be inexact, but should never underflow. -// FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds. -pub struct FallbackExtendedS(#[allow(unused)] F); -type FallbackExtended = ieee::IeeeFloat>; -impl ieee::Semantics for FallbackExtendedS { - // Forbid any conversion to/from bits. - const BITS: usize = 0; - const PRECISION: usize = Fallback::::PRECISION; - const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt; -} - -impl From> for DoubleFloat -where - F: FloatConvert>, - FallbackExtended: FloatConvert, -{ - fn from(x: Fallback) -> Self { - let mut status; - let mut loses_info = false; - - let extended: FallbackExtended = unpack!(status=, x.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - - let a = unpack!(status=, extended.convert(&mut loses_info)); - assert_eq!(status - Status::INEXACT, Status::OK); - - // If conversion was exact or resulted in a special case, we're done; - // just set the second double to zero. Otherwise, re-convert back to - // the extended format and compute the difference. This now should - // convert exactly to double. - let b = if a.is_finite_non_zero() && loses_info { - let u: FallbackExtended = unpack!(status=, a.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - let v = unpack!(status=, extended - u); - assert_eq!(status, Status::OK); - let v = unpack!(status=, v.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - v - } else { - F::ZERO - }; - - DoubleFloat(a, b) - } -} - -impl> From> for Fallback { - fn from(DoubleFloat(a, b): DoubleFloat) -> Self { - let mut status; - let mut loses_info = false; - - // Get the first F and convert to our format. - let a = unpack!(status=, a.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - - // Unless we have a special case, add in second F. - if a.is_finite_non_zero() { - let b = unpack!(status=, b.convert(&mut loses_info)); - assert_eq!((status, loses_info), (Status::OK, false)); - - (a + b).value - } else { - a - } - } -} - -float_common_impls!(DoubleFloat); - -impl Neg for DoubleFloat { - type Output = Self; - fn neg(self) -> Self { - if self.1.is_finite_non_zero() { - DoubleFloat(-self.0, -self.1) - } else { - DoubleFloat(-self.0, self.1) - } - } -} - -impl>> fmt::Display for DoubleFloat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&Fallback::from(*self), f) - } -} - -impl>> Float for DoubleFloat -where - Self: From>, -{ - const BITS: usize = F::BITS * 2; - const PRECISION: usize = Fallback::::PRECISION; - const MAX_EXP: ExpInt = Fallback::::MAX_EXP; - const MIN_EXP: ExpInt = Fallback::::MIN_EXP; - - const ZERO: Self = DoubleFloat(F::ZERO, F::ZERO); - - const INFINITY: Self = DoubleFloat(F::INFINITY, F::ZERO); - - // FIXME(eddyb) remove when qnan becomes const fn. - const NAN: Self = DoubleFloat(F::NAN, F::ZERO); - - fn qnan(payload: Option) -> Self { - DoubleFloat(F::qnan(payload), F::ZERO) - } - - fn snan(payload: Option) -> Self { - DoubleFloat(F::snan(payload), F::ZERO) - } - - fn largest() -> Self { - let status; - let mut r = DoubleFloat(F::largest(), F::largest()); - r.1 = r.1.scalbn(-(F::PRECISION as ExpInt + 1)); - r.1 = unpack!(status=, r.1.next_down()); - assert_eq!(status, Status::OK); - r - } - - const SMALLEST: Self = DoubleFloat(F::SMALLEST, F::ZERO); - - fn smallest_normalized() -> Self { - DoubleFloat(F::smallest_normalized().scalbn(F::PRECISION as ExpInt), F::ZERO) - } - - // Implement addition, subtraction, multiplication and division based on: - // "Software for Doubled-Precision Floating-Point Computations", - // by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283. - - fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd { - match (self.category(), rhs.category()) { - (Category::Infinity, Category::Infinity) => { - if self.is_negative() != rhs.is_negative() { - Status::INVALID_OP.and(Self::NAN.copy_sign(self)) - } else { - Status::OK.and(self) - } - } - - (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => { - Status::OK.and(self) - } - - (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs), - - (Category::Normal, Category::Normal) => { - let mut status = Status::OK; - let (a, aa, c, cc) = (self.0, self.1, rhs.0, rhs.1); - let mut z = a; - z = unpack!(status|=, z.add_r(c, round)); - if !z.is_finite() { - if !z.is_infinite() { - return status.and(DoubleFloat(z, F::ZERO)); - } - status = Status::OK; - let a_cmp_c = a.cmp_abs_normal(c); - z = cc; - z = unpack!(status|=, z.add_r(aa, round)); - if a_cmp_c == Ordering::Greater { - // z = cc + aa + c + a; - z = unpack!(status|=, z.add_r(c, round)); - z = unpack!(status|=, z.add_r(a, round)); - } else { - // z = cc + aa + a + c; - z = unpack!(status|=, z.add_r(a, round)); - z = unpack!(status|=, z.add_r(c, round)); - } - if !z.is_finite() { - return status.and(DoubleFloat(z, F::ZERO)); - } - self.0 = z; - let mut zz = aa; - zz = unpack!(status|=, zz.add_r(cc, round)); - if a_cmp_c == Ordering::Greater { - // self.1 = a - z + c + zz; - self.1 = a; - self.1 = unpack!(status|=, self.1.sub_r(z, round)); - self.1 = unpack!(status|=, self.1.add_r(c, round)); - self.1 = unpack!(status|=, self.1.add_r(zz, round)); - } else { - // self.1 = c - z + a + zz; - self.1 = c; - self.1 = unpack!(status|=, self.1.sub_r(z, round)); - self.1 = unpack!(status|=, self.1.add_r(a, round)); - self.1 = unpack!(status|=, self.1.add_r(zz, round)); - } - } else { - // q = a - z; - let mut q = a; - q = unpack!(status|=, q.sub_r(z, round)); - - // zz = q + c + (a - (q + z)) + aa + cc; - // Compute a - (q + z) as -((q + z) - a) to avoid temporary copies. - let mut zz = q; - zz = unpack!(status|=, zz.add_r(c, round)); - q = unpack!(status|=, q.add_r(z, round)); - q = unpack!(status|=, q.sub_r(a, round)); - q = -q; - zz = unpack!(status|=, zz.add_r(q, round)); - zz = unpack!(status|=, zz.add_r(aa, round)); - zz = unpack!(status|=, zz.add_r(cc, round)); - if zz.is_zero() && !zz.is_negative() { - return Status::OK.and(DoubleFloat(z, F::ZERO)); - } - self.0 = z; - self.0 = unpack!(status|=, self.0.add_r(zz, round)); - if !self.0.is_finite() { - self.1 = F::ZERO; - return status.and(self); - } - self.1 = z; - self.1 = unpack!(status|=, self.1.sub_r(self.0, round)); - self.1 = unpack!(status|=, self.1.add_r(zz, round)); - } - status.and(self) - } - } - } - - fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd { - // Interesting observation: For special categories, finding the lowest - // common ancestor of the following layered graph gives the correct - // return category: - // - // NaN - // / \ - // Zero Inf - // \ / - // Normal - // - // e.g., NaN * NaN = NaN - // Zero * Inf = NaN - // Normal * Zero = Zero - // Normal * Inf = Inf - match (self.category(), rhs.category()) { - (Category::NaN, _) => Status::OK.and(self), - - (_, Category::NaN) => Status::OK.and(rhs), - - (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => { - Status::OK.and(Self::NAN) - } - - (Category::Zero | Category::Infinity, _) => Status::OK.and(self), - - (_, Category::Zero | Category::Infinity) => Status::OK.and(rhs), - - (Category::Normal, Category::Normal) => { - let mut status = Status::OK; - let (a, b, c, d) = (self.0, self.1, rhs.0, rhs.1); - // t = a * c - let mut t = a; - t = unpack!(status|=, t.mul_r(c, round)); - if !t.is_finite_non_zero() { - return status.and(DoubleFloat(t, F::ZERO)); - } - - // tau = fmsub(a, c, t), that is -fmadd(-a, c, t). - let mut tau = a; - tau = unpack!(status|=, tau.mul_add_r(c, -t, round)); - // v = a * d - let mut v = a; - v = unpack!(status|=, v.mul_r(d, round)); - // w = b * c - let mut w = b; - w = unpack!(status|=, w.mul_r(c, round)); - v = unpack!(status|=, v.add_r(w, round)); - // tau += v + w - tau = unpack!(status|=, tau.add_r(v, round)); - // u = t + tau - let mut u = t; - u = unpack!(status|=, u.add_r(tau, round)); - - self.0 = u; - if !u.is_finite() { - self.1 = F::ZERO; - } else { - // self.1 = (t - u) + tau - t = unpack!(status|=, t.sub_r(u, round)); - t = unpack!(status|=, t.add_r(tau, round)); - self.1 = t; - } - status.and(self) - } - } - } - - fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd { - Fallback::from(self) - .mul_add_r(Fallback::from(multiplicand), Fallback::from(addend), round) - .map(Self::from) - } - - fn div_r(self, rhs: Self, round: Round) -> StatusAnd { - Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from) - } - - fn c_fmod(self, rhs: Self) -> StatusAnd { - Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from) - } - - fn round_to_integral(self, round: Round) -> StatusAnd { - Fallback::from(self).round_to_integral(round).map(Self::from) - } - - fn next_up(self) -> StatusAnd { - Fallback::from(self).next_up().map(Self::from) - } - - fn from_bits(input: u128) -> Self { - let (a, b) = (input, input >> F::BITS); - DoubleFloat(F::from_bits(a & ((1 << F::BITS) - 1)), F::from_bits(b & ((1 << F::BITS) - 1))) - } - - fn from_u128_r(input: u128, round: Round) -> StatusAnd { - Fallback::from_u128_r(input, round).map(Self::from) - } - - fn from_str_r(s: &str, round: Round) -> Result, ParseError> { - Fallback::from_str_r(s, round).map(|r| r.map(Self::from)) - } - - fn to_bits(self) -> u128 { - self.0.to_bits() | (self.1.to_bits() << F::BITS) - } - - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd { - Fallback::from(self).to_u128_r(width, round, is_exact) - } - - fn cmp_abs_normal(self, rhs: Self) -> Ordering { - self.0.cmp_abs_normal(rhs.0).then_with(|| { - let result = self.1.cmp_abs_normal(rhs.1); - if result != Ordering::Equal { - let against = self.0.is_negative() ^ self.1.is_negative(); - let rhs_against = rhs.0.is_negative() ^ rhs.1.is_negative(); - (!against) - .cmp(&!rhs_against) - .then_with(|| if against { result.reverse() } else { result }) - } else { - result - } - }) - } - - fn bitwise_eq(self, rhs: Self) -> bool { - self.0.bitwise_eq(rhs.0) && self.1.bitwise_eq(rhs.1) - } - - fn is_negative(self) -> bool { - self.0.is_negative() - } - - fn is_denormal(self) -> bool { - self.category() == Category::Normal - && (self.0.is_denormal() || self.0.is_denormal() || - // (double)(Hi + Lo) == Hi defines a normal number. - !(self.0 + self.1).value.bitwise_eq(self.0)) - } - - fn is_signaling(self) -> bool { - self.0.is_signaling() - } - - fn category(self) -> Category { - self.0.category() - } - - fn get_exact_inverse(self) -> Option { - Fallback::from(self).get_exact_inverse().map(Self::from) - } - - fn ilogb(self) -> ExpInt { - self.0.ilogb() - } - - fn scalbn_r(self, exp: ExpInt, round: Round) -> Self { - DoubleFloat(self.0.scalbn_r(exp, round), self.1.scalbn_r(exp, round)) - } - - fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self { - let a = self.0.frexp_r(exp, round); - let mut b = self.1; - if self.category() == Category::Normal { - b = b.scalbn_r(-*exp, round); - } - DoubleFloat(a, b) - } -} diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs deleted file mode 100644 index f8fac0c2358c..000000000000 --- a/compiler/rustc_apfloat/tests/ieee.rs +++ /dev/null @@ -1,3301 +0,0 @@ -// ignore-tidy-filelength - -use rustc_apfloat::ieee::{Double, Half, Quad, Single, X87DoubleExtended}; -use rustc_apfloat::unpack; -use rustc_apfloat::{Category, ExpInt, IEK_INF, IEK_NAN, IEK_ZERO}; -use rustc_apfloat::{Float, FloatConvert, ParseError, Round, Status}; - -trait SingleExt { - fn from_f32(input: f32) -> Self; - fn to_f32(self) -> f32; -} - -impl SingleExt for Single { - fn from_f32(input: f32) -> Self { - Self::from_bits(input.to_bits() as u128) - } - - fn to_f32(self) -> f32 { - f32::from_bits(self.to_bits() as u32) - } -} - -trait DoubleExt { - fn from_f64(input: f64) -> Self; - fn to_f64(self) -> f64; -} - -impl DoubleExt for Double { - fn from_f64(input: f64) -> Self { - Self::from_bits(input.to_bits() as u128) - } - - fn to_f64(self) -> f64 { - f64::from_bits(self.to_bits() as u64) - } -} - -#[test] -fn is_signaling() { - // We test qNaN, -qNaN, +sNaN, -sNaN with and without payloads. - let payload = 4; - assert!(!Single::qnan(None).is_signaling()); - assert!(!(-Single::qnan(None)).is_signaling()); - assert!(!Single::qnan(Some(payload)).is_signaling()); - assert!(!(-Single::qnan(Some(payload))).is_signaling()); - assert!(Single::snan(None).is_signaling()); - assert!((-Single::snan(None)).is_signaling()); - assert!(Single::snan(Some(payload)).is_signaling()); - assert!((-Single::snan(Some(payload))).is_signaling()); -} - -#[test] -fn next() { - // 1. Test Special Cases Values. - // - // Test all special values for nextUp and nextDown perscribed by IEEE-754R - // 2008. These are: - // 1. +inf - // 2. -inf - // 3. largest - // 4. -largest - // 5. smallest - // 6. -smallest - // 7. qNaN - // 8. sNaN - // 9. +0 - // 10. -0 - - let mut status; - - // nextUp(+inf) = +inf. - let test = unpack!(status=, Quad::INFINITY.next_up()); - let expected = Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+inf) = -nextUp(-inf) = -(-largest) = largest - let test = unpack!(status=, Quad::INFINITY.next_down()); - let expected = Quad::largest(); - assert_eq!(status, Status::OK); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-inf) = -largest - let test = unpack!(status=, (-Quad::INFINITY).next_up()); - let expected = -Quad::largest(); - assert_eq!(status, Status::OK); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-inf) = -nextUp(+inf) = -(+inf) = -inf. - let test = unpack!(status=, (-Quad::INFINITY).next_down()); - let expected = -Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite() && test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(largest) = +inf - let test = unpack!(status=, Quad::largest().next_up()); - let expected = Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite() && !test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(largest) = -nextUp(-largest) - // = -(-largest + inc) - // = largest - inc. - let test = unpack!(status=, Quad::largest().next_down()); - let expected = "0x1.fffffffffffffffffffffffffffep+16383".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_infinite() && !test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-largest) = -largest + inc. - let test = unpack!(status=, (-Quad::largest()).next_up()); - let expected = "-0x1.fffffffffffffffffffffffffffep+16383".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-largest) = -nextUp(largest) = -(inf) = -inf. - let test = unpack!(status=, (-Quad::largest()).next_down()); - let expected = -Quad::INFINITY; - assert_eq!(status, Status::OK); - assert!(test.is_infinite() && test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(smallest) = smallest + inc. - let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x0.0000000000000000000000000002p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(smallest) = -nextUp(-smallest) = -(-0) = +0. - let test = unpack!(status=, "0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = Quad::ZERO; - assert_eq!(status, Status::OK); - assert!(test.is_pos_zero()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-smallest) = -0. - let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = -Quad::ZERO; - assert_eq!(status, Status::OK); - assert!(test.is_neg_zero()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-smallest) = -nextUp(smallest) = -smallest - inc. - let test = unpack!(status=, "-0x0.0000000000000000000000000001p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x0.0000000000000000000000000002p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(qNaN) = qNaN - let test = unpack!(status=, Quad::qnan(None).next_up()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(qNaN) = qNaN - let test = unpack!(status=, Quad::qnan(None).next_down()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(sNaN) = qNaN - let test = unpack!(status=, Quad::snan(None).next_up()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::INVALID_OP); - assert!(test.bitwise_eq(expected)); - - // nextDown(sNaN) = qNaN - let test = unpack!(status=, Quad::snan(None).next_down()); - let expected = Quad::qnan(None); - assert_eq!(status, Status::INVALID_OP); - assert!(test.bitwise_eq(expected)); - - // nextUp(+0) = +smallest - let test = unpack!(status=, Quad::ZERO.next_up()); - let expected = Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(+0) = -nextUp(-0) = -smallest - let test = unpack!(status=, Quad::ZERO.next_down()); - let expected = -Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(-0) = +smallest - let test = unpack!(status=, (-Quad::ZERO).next_up()); - let expected = Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-0) = -nextUp(0) = -smallest - let test = unpack!(status=, (-Quad::ZERO).next_down()); - let expected = -Quad::SMALLEST; - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // 2. Binade Boundary Tests. - - // 2a. Test denormal <-> normal binade boundaries. - // * nextUp(+Largest Denormal) -> +Smallest Normal. - // * nextDown(-Largest Denormal) -> -Smallest Normal. - // * nextUp(-Smallest Normal) -> -Largest Denormal. - // * nextDown(+Smallest Normal) -> +Largest Denormal. - - // nextUp(+Largest Denormal) -> +Smallest Normal. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1.0000000000000000000000000000p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Largest Denormal) -> -Smallest Normal. - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1.0000000000000000000000000000p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-Smallest Normal) -> -Largest Denormal. - let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x0.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Smallest Normal) -> +Largest Denormal. - let test = unpack!(status=, "+0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "+0x0.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - // 2b. Test normal <-> normal binade boundaries. - // * nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. - // * nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. - // * nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. - // * nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. - - // nextUp(-Normal Binade Boundary) -> -Normal Binade Boundary + 1. - let test = unpack!(status=, "-0x1p+1".parse::().unwrap().next_up()); - let expected = "-0x1.ffffffffffffffffffffffffffffp+0".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Normal Binade Boundary) -> +Normal Binade Boundary - 1. - let test = unpack!(status=, "0x1p+1".parse::().unwrap().next_down()); - let expected = "0x1.ffffffffffffffffffffffffffffp+0".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(+Normal Binade Boundary - 1) -> +Normal Binade Boundary. - let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp+0" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1p+1".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Normal Binade Boundary + 1) -> -Normal Binade Boundary. - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp+0" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1p+1".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // 2c. Test using next at binade boundaries with a direction away from the - // binade boundary. Away from denormal <-> normal boundaries. - // - // This is to make sure that even though we are at a binade boundary, since - // we are rounding away, we do not trigger the binade boundary code. Thus we - // test: - // * nextUp(-Largest Denormal) -> -Largest Denormal + inc. - // * nextDown(+Largest Denormal) -> +Largest Denormal - inc. - // * nextUp(+Smallest Normal) -> +Smallest Normal + inc. - // * nextDown(-Smallest Normal) -> -Smallest Normal - inc. - - // nextUp(-Largest Denormal) -> -Largest Denormal + inc. - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x0.fffffffffffffffffffffffffffep-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Largest Denormal) -> +Largest Denormal - inc. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "0x0.fffffffffffffffffffffffffffep-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(+Smallest Normal) -> +Smallest Normal + inc. - let test = unpack!(status=, "0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1.0000000000000000000000000001p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Smallest Normal) -> -Smallest Normal - inc. - let test = unpack!(status=, "-0x1.0000000000000000000000000000p-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1.0000000000000000000000000001p-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // 2d. Test values which cause our exponent to go to min exponent. This - // is to ensure that guards in the code to check for min exponent - // trigger properly. - // * nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 - // * nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> - // -0x1p-16381 - // * nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16382 - // * nextDown(0x1p-16382) -> 0x1.ffffffffffffffffffffffffffffp-16382 - - // nextUp(-0x1p-16381) -> -0x1.ffffffffffffffffffffffffffffp-16382 - let test = unpack!(status=, "-0x1p-16381".parse::().unwrap().next_up()); - let expected = "-0x1.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(-0x1.ffffffffffffffffffffffffffffp-16382) -> - // -0x1p-16381 - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1p-16381".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextUp(0x1.ffffffffffffffffffffffffffffp-16382) -> 0x1p-16381 - let test = unpack!(status=, "0x1.ffffffffffffffffffffffffffffp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1p-16381".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382 - let test = unpack!(status=, "0x1p-16381".parse::().unwrap().next_down()); - let expected = "0x1.ffffffffffffffffffffffffffffp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // 3. Now we test both denormal/normal computation which will not cause us - // to go across binade boundaries. Specifically we test: - // * nextUp(+Denormal) -> +Denormal. - // * nextDown(+Denormal) -> +Denormal. - // * nextUp(-Denormal) -> -Denormal. - // * nextDown(-Denormal) -> -Denormal. - // * nextUp(+Normal) -> +Normal. - // * nextDown(+Normal) -> +Normal. - // * nextUp(-Normal) -> -Normal. - // * nextDown(-Normal) -> -Normal. - - // nextUp(+Denormal) -> +Denormal. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "0x0.ffffffffffffffffffffffff000dp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Denormal) -> +Denormal. - let test = unpack!(status=, "0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "0x0.ffffffffffffffffffffffff000bp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-Denormal) -> -Denormal. - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x0.ffffffffffffffffffffffff000bp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Denormal) -> -Denormal - let test = unpack!(status=, "-0x0.ffffffffffffffffffffffff000cp-16382" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x0.ffffffffffffffffffffffff000dp-16382".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(+Normal) -> +Normal. - let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_up()); - let expected = "0x1.ffffffffffffffffffffffff000dp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(+Normal) -> +Normal. - let test = unpack!(status=, "0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_down()); - let expected = "0x1.ffffffffffffffffffffffff000bp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextUp(-Normal) -> -Normal. - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_up()); - let expected = "-0x1.ffffffffffffffffffffffff000bp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - - // nextDown(-Normal) -> -Normal. - let test = unpack!(status=, "-0x1.ffffffffffffffffffffffff000cp-16000" - .parse::() - .unwrap() - .next_down()); - let expected = "-0x1.ffffffffffffffffffffffff000dp-16000".parse::().unwrap(); - assert_eq!(status, Status::OK); - assert!(!test.is_denormal()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); -} - -#[test] -fn fma() { - { - let mut f1 = Single::from_f32(14.5); - let f2 = Single::from_f32(-14.5); - let f3 = Single::from_f32(225.0); - f1 = f1.mul_add(f2, f3).value; - assert_eq!(14.75, f1.to_f32()); - } - - { - let val2 = Single::from_f32(2.0); - let mut f1 = Single::from_f32(1.17549435e-38); - let mut f2 = Single::from_f32(1.17549435e-38); - f1 /= val2; - f2 /= val2; - let f3 = Single::from_f32(12.0); - f1 = f1.mul_add(f2, f3).value; - assert_eq!(12.0, f1.to_f32()); - } - - // Test for correct zero sign when answer is exactly zero. - // fma(1.0, -1.0, 1.0) -> +ve 0. - { - let mut f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(-1.0); - let f3 = Double::from_f64(1.0); - f1 = f1.mul_add(f2, f3).value; - assert!(!f1.is_negative() && f1.is_zero()); - } - - // Test for correct zero sign when answer is exactly zero and rounding towards - // negative. - // fma(1.0, -1.0, 1.0) -> +ve 0. - { - let mut f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(-1.0); - let f3 = Double::from_f64(1.0); - f1 = f1.mul_add_r(f2, f3, Round::TowardNegative).value; - assert!(f1.is_negative() && f1.is_zero()); - } - - // Test for correct (in this case -ve) sign when adding like signed zeros. - // Test fma(0.0, -0.0, -0.0) -> -ve 0. - { - let mut f1 = Double::from_f64(0.0); - let f2 = Double::from_f64(-0.0); - let f3 = Double::from_f64(-0.0); - f1 = f1.mul_add(f2, f3).value; - assert!(f1.is_negative() && f1.is_zero()); - } - - // Test -ve sign preservation when small negative results underflow. - { - let mut f1 = "-0x1p-1074".parse::().unwrap(); - let f2 = "+0x1p-1074".parse::().unwrap(); - let f3 = Double::from_f64(0.0); - f1 = f1.mul_add(f2, f3).value; - assert!(f1.is_negative() && f1.is_zero()); - } - - // Test x87 extended precision case from https://llvm.org/PR20728. - { - let mut m1 = X87DoubleExtended::from_u128(1).value; - let m2 = X87DoubleExtended::from_u128(1).value; - let a = X87DoubleExtended::from_u128(3).value; - - let mut loses_info = false; - m1 = m1.mul_add(m2, a).value; - let r: Single = m1.convert(&mut loses_info).value; - assert!(!loses_info); - assert_eq!(4.0, r.to_f32()); - } -} - -#[test] -fn issue_69532() { - let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128); - let mut loses_info = false; - let sta = f.convert(&mut loses_info); - let r: Single = sta.value; - assert!(loses_info); - assert!(r.is_nan()); - assert_eq!(sta.status, Status::INVALID_OP); -} - -#[test] -fn min_num() { - let f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(2.0); - let nan = Double::NAN; - - assert_eq!(1.0, f1.min(f2).to_f64()); - assert_eq!(1.0, f2.min(f1).to_f64()); - assert_eq!(1.0, f1.min(nan).to_f64()); - assert_eq!(1.0, nan.min(f1).to_f64()); -} - -#[test] -fn max_num() { - let f1 = Double::from_f64(1.0); - let f2 = Double::from_f64(2.0); - let nan = Double::NAN; - - assert_eq!(2.0, f1.max(f2).to_f64()); - assert_eq!(2.0, f2.max(f1).to_f64()); - assert_eq!(1.0, f1.max(nan).to_f64()); - assert_eq!(1.0, nan.max(f1).to_f64()); -} - -#[test] -fn denormal() { - // Test single precision - { - assert!(!Single::from_f32(0.0).is_denormal()); - - let mut t = "1.17549435082228750797e-38".parse::().unwrap(); - assert!(!t.is_denormal()); - - let val2 = Single::from_f32(2.0e0); - t /= val2; - assert!(t.is_denormal()); - } - - // Test double precision - { - assert!(!Double::from_f64(0.0).is_denormal()); - - let mut t = "2.22507385850720138309e-308".parse::().unwrap(); - assert!(!t.is_denormal()); - - let val2 = Double::from_f64(2.0e0); - t /= val2; - assert!(t.is_denormal()); - } - - // Test Intel double-ext - { - assert!(!X87DoubleExtended::from_u128(0).value.is_denormal()); - - let mut t = "3.36210314311209350626e-4932".parse::().unwrap(); - assert!(!t.is_denormal()); - - t /= X87DoubleExtended::from_u128(2).value; - assert!(t.is_denormal()); - } - - // Test quadruple precision - { - assert!(!Quad::from_u128(0).value.is_denormal()); - - let mut t = "3.36210314311209350626267781732175260e-4932".parse::().unwrap(); - assert!(!t.is_denormal()); - - t /= Quad::from_u128(2).value; - assert!(t.is_denormal()); - } -} - -#[test] -fn decimal_strings_without_null_terminators() { - // Make sure that we can parse strings without null terminators. - // rdar://14323230. - let val = "0.00"[..3].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.01"[..3].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.09"[..3].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.095"[..4].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.09); - let val = "0.00e+3"[..7].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.00); - let val = "0e+3"[..4].parse::().unwrap(); - assert_eq!(val.to_f64(), 0.00); -} - -#[test] -fn from_zero_decimal_string() { - assert_eq!(0.0, "0".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "00000.".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+00000.".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-00000.".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".00000".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.00000".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.00000".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0000.00000".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0000.00000".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0000.00000".parse::().unwrap().to_f64()); -} - -#[test] -fn from_zero_decimal_single_exponent_string() { - assert_eq!(0.0, "0e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, ".0e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+.0e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "000.0000e1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+000.0000e+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-000.0000e+1".parse::().unwrap().to_f64()); -} - -#[test] -fn from_zero_decimal_large_exponent_string() { - assert_eq!(0.0, "0e1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e1234".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e+1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e+1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e+1234".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0e-1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0e-1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0e-1234".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "000.0000e1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "000.0000e-1234".parse::().unwrap().to_f64()); -} - -#[test] -fn from_zero_hexadecimal_string() { - assert_eq!(0.0, "0x0p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p+1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p+1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p-1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p-1".parse::().unwrap().to_f64()); - - assert_eq!(0.0, "0x00000.p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0000.00000p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x.00000p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0.p1".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0p1234".parse::().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x00000.p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0000.00000p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x.00000p1234".parse::().unwrap().to_f64()); - assert_eq!(0.0, "0x0.p1234".parse::().unwrap().to_f64()); -} - -#[test] -fn from_decimal_string() { - assert_eq!(1.0, "1".parse::().unwrap().to_f64()); - assert_eq!(2.0, "2.".parse::().unwrap().to_f64()); - assert_eq!(0.5, ".5".parse::().unwrap().to_f64()); - assert_eq!(1.0, "1.0".parse::().unwrap().to_f64()); - assert_eq!(-2.0, "-2".parse::().unwrap().to_f64()); - assert_eq!(-4.0, "-4.".parse::().unwrap().to_f64()); - assert_eq!(-0.5, "-.5".parse::().unwrap().to_f64()); - assert_eq!(-1.5, "-1.5".parse::().unwrap().to_f64()); - assert_eq!(1.25e12, "1.25e12".parse::().unwrap().to_f64()); - assert_eq!(1.25e+12, "1.25e+12".parse::().unwrap().to_f64()); - assert_eq!(1.25e-12, "1.25e-12".parse::().unwrap().to_f64()); - assert_eq!(1024.0, "1024.".parse::().unwrap().to_f64()); - assert_eq!(1024.05, "1024.05000".parse::().unwrap().to_f64()); - assert_eq!(0.05, ".05000".parse::().unwrap().to_f64()); - assert_eq!(2.0, "2.".parse::().unwrap().to_f64()); - assert_eq!(2.0e2, "2.e2".parse::().unwrap().to_f64()); - assert_eq!(2.0e+2, "2.e+2".parse::().unwrap().to_f64()); - assert_eq!(2.0e-2, "2.e-2".parse::().unwrap().to_f64()); - assert_eq!(2.05e2, "002.05000e2".parse::().unwrap().to_f64()); - assert_eq!(2.05e+2, "002.05000e+2".parse::().unwrap().to_f64()); - assert_eq!(2.05e-2, "002.05000e-2".parse::().unwrap().to_f64()); - assert_eq!(2.05e12, "002.05000e12".parse::().unwrap().to_f64()); - assert_eq!(2.05e+12, "002.05000e+12".parse::().unwrap().to_f64()); - assert_eq!(2.05e-12, "002.05000e-12".parse::().unwrap().to_f64()); - - // These are "carefully selected" to overflow the fast log-base - // calculations in the implementation. - assert!("99e99999".parse::().unwrap().is_infinite()); - assert!("-99e99999".parse::().unwrap().is_infinite()); - assert!("1e-99999".parse::().unwrap().is_pos_zero()); - assert!("-1e-99999".parse::().unwrap().is_neg_zero()); - - assert_eq!(2.71828, "2.71828".parse::().unwrap().to_f64()); -} - -#[test] -fn from_hexadecimal_string() { - assert_eq!(1.0, "0x1p0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p0".parse::().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p0".parse::().unwrap().to_f64()); - - assert_eq!(1.0, "0x1p+0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p+0".parse::().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p+0".parse::().unwrap().to_f64()); - - assert_eq!(1.0, "0x1p-0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p-0".parse::().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p-0".parse::().unwrap().to_f64()); - - assert_eq!(2.0, "0x1p1".parse::().unwrap().to_f64()); - assert_eq!(2.0, "+0x1p1".parse::().unwrap().to_f64()); - assert_eq!(-2.0, "-0x1p1".parse::().unwrap().to_f64()); - - assert_eq!(2.0, "0x1p+1".parse::().unwrap().to_f64()); - assert_eq!(2.0, "+0x1p+1".parse::().unwrap().to_f64()); - assert_eq!(-2.0, "-0x1p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.5, "0x1p-1".parse::().unwrap().to_f64()); - assert_eq!(0.5, "+0x1p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.5, "-0x1p-1".parse::().unwrap().to_f64()); - - assert_eq!(3.0, "0x1.8p1".parse::().unwrap().to_f64()); - assert_eq!(3.0, "+0x1.8p1".parse::().unwrap().to_f64()); - assert_eq!(-3.0, "-0x1.8p1".parse::().unwrap().to_f64()); - - assert_eq!(3.0, "0x1.8p+1".parse::().unwrap().to_f64()); - assert_eq!(3.0, "+0x1.8p+1".parse::().unwrap().to_f64()); - assert_eq!(-3.0, "-0x1.8p+1".parse::().unwrap().to_f64()); - - assert_eq!(0.75, "0x1.8p-1".parse::().unwrap().to_f64()); - assert_eq!(0.75, "+0x1.8p-1".parse::().unwrap().to_f64()); - assert_eq!(-0.75, "-0x1.8p-1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000.000p1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000.000p1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000.000p1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000.000p+1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000.000p+1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000.000p+1".parse::().unwrap().to_f64()); - - assert_eq!(2048.0, "0x1000.000p-1".parse::().unwrap().to_f64()); - assert_eq!(2048.0, "+0x1000.000p-1".parse::().unwrap().to_f64()); - assert_eq!(-2048.0, "-0x1000.000p-1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000p1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000p1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000p1".parse::().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000p+1".parse::().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000p+1".parse::().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000p+1".parse::().unwrap().to_f64()); - - assert_eq!(2048.0, "0x1000p-1".parse::().unwrap().to_f64()); - assert_eq!(2048.0, "+0x1000p-1".parse::().unwrap().to_f64()); - assert_eq!(-2048.0, "-0x1000p-1".parse::().unwrap().to_f64()); - - assert_eq!(16384.0, "0x10p10".parse::().unwrap().to_f64()); - assert_eq!(16384.0, "+0x10p10".parse::().unwrap().to_f64()); - assert_eq!(-16384.0, "-0x10p10".parse::().unwrap().to_f64()); - - assert_eq!(16384.0, "0x10p+10".parse::().unwrap().to_f64()); - assert_eq!(16384.0, "+0x10p+10".parse::().unwrap().to_f64()); - assert_eq!(-16384.0, "-0x10p+10".parse::().unwrap().to_f64()); - - assert_eq!(0.015625, "0x10p-10".parse::().unwrap().to_f64()); - assert_eq!(0.015625, "+0x10p-10".parse::().unwrap().to_f64()); - assert_eq!(-0.015625, "-0x10p-10".parse::().unwrap().to_f64()); - - assert_eq!(1.0625, "0x1.1p0".parse::().unwrap().to_f64()); - assert_eq!(1.0, "0x1p0".parse::().unwrap().to_f64()); - - assert_eq!( - "0x1p-150".parse::().unwrap().to_f64(), - "+0x800000000000000001.p-221".parse::().unwrap().to_f64() - ); - assert_eq!( - 2251799813685248.5, - "0x80000000000004000000.010p-28".parse::().unwrap().to_f64() - ); -} - -#[test] -fn to_string() { - let to_string = |d: f64, precision: usize, width: usize| { - let x = Double::from_f64(d); - if precision == 0 { - format!("{:1$}", x, width) - } else { - format!("{:2$.1$}", x, precision, width) - } - }; - assert_eq!("10", to_string(10.0, 6, 3)); - assert_eq!("1.0E+1", to_string(10.0, 6, 0)); - assert_eq!("10100", to_string(1.01E+4, 5, 2)); - assert_eq!("1.01E+4", to_string(1.01E+4, 4, 2)); - assert_eq!("1.01E+4", to_string(1.01E+4, 5, 1)); - assert_eq!("0.0101", to_string(1.01E-2, 5, 2)); - assert_eq!("0.0101", to_string(1.01E-2, 4, 2)); - assert_eq!("1.01E-2", to_string(1.01E-2, 5, 1)); - assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3)); - assert_eq!("4.9406564584124654E-324", to_string(4.9406564584124654e-324, 0, 3)); - assert_eq!("873.18340000000001", to_string(873.1834, 0, 1)); - assert_eq!("8.7318340000000001E+2", to_string(873.1834, 0, 0)); - assert_eq!("1.7976931348623157E+308", to_string(1.7976931348623157E+308, 0, 0)); - - let to_string = |d: f64, precision: usize, width: usize| { - let x = Double::from_f64(d); - if precision == 0 { - format!("{:#1$}", x, width) - } else { - format!("{:#2$.1$}", x, precision, width) - } - }; - assert_eq!("10", to_string(10.0, 6, 3)); - assert_eq!("1.000000e+01", to_string(10.0, 6, 0)); - assert_eq!("10100", to_string(1.01E+4, 5, 2)); - assert_eq!("1.0100e+04", to_string(1.01E+4, 4, 2)); - assert_eq!("1.01000e+04", to_string(1.01E+4, 5, 1)); - assert_eq!("0.0101", to_string(1.01E-2, 5, 2)); - assert_eq!("0.0101", to_string(1.01E-2, 4, 2)); - assert_eq!("1.01000e-02", to_string(1.01E-2, 5, 1)); - assert_eq!("0.78539816339744828", to_string(0.78539816339744830961, 0, 3)); - assert_eq!("4.94065645841246540e-324", to_string(4.9406564584124654e-324, 0, 3)); - assert_eq!("873.18340000000001", to_string(873.1834, 0, 1)); - assert_eq!("8.73183400000000010e+02", to_string(873.1834, 0, 0)); - assert_eq!("1.79769313486231570e+308", to_string(1.7976931348623157E+308, 0, 0)); -} - -#[test] -fn to_integer() { - let mut is_exact = false; - - assert_eq!( - Status::OK.and(10), - "10".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(is_exact); - - assert_eq!( - Status::INVALID_OP.and(0), - "-10".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INVALID_OP.and(31), - "32".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INEXACT.and(7), - "7.9".parse::().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::OK.and(-10), - "-10".parse::().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(is_exact); - - assert_eq!( - Status::INVALID_OP.and(-16), - "-17".parse::().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INVALID_OP.and(15), - "16".parse::().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); -} - -#[test] -fn nan() { - fn nanbits(signaling: bool, negative: bool, fill: u128) -> u128 { - let x = if signaling { T::snan(Some(fill)) } else { T::qnan(Some(fill)) }; - if negative { (-x).to_bits() } else { x.to_bits() } - } - - assert_eq!(0x7fc00000, nanbits::(false, false, 0)); - assert_eq!(0xffc00000, nanbits::(false, true, 0)); - assert_eq!(0x7fc0ae72, nanbits::(false, false, 0xae72)); - assert_eq!(0x7fffae72, nanbits::(false, false, 0xffffae72)); - assert_eq!(0x7fa00000, nanbits::(true, false, 0)); - assert_eq!(0xffa00000, nanbits::(true, true, 0)); - assert_eq!(0x7f80ae72, nanbits::(true, false, 0xae72)); - assert_eq!(0x7fbfae72, nanbits::(true, false, 0xffffae72)); - - assert_eq!(0x7ff8000000000000, nanbits::(false, false, 0)); - assert_eq!(0xfff8000000000000, nanbits::(false, true, 0)); - assert_eq!(0x7ff800000000ae72, nanbits::(false, false, 0xae72)); - assert_eq!(0x7fffffffffffae72, nanbits::(false, false, 0xffffffffffffae72)); - assert_eq!(0x7ff4000000000000, nanbits::(true, false, 0)); - assert_eq!(0xfff4000000000000, nanbits::(true, true, 0)); - assert_eq!(0x7ff000000000ae72, nanbits::(true, false, 0xae72)); - assert_eq!(0x7ff7ffffffffae72, nanbits::(true, false, 0xffffffffffffae72)); -} - -#[test] -fn string_decimal_death() { - assert_eq!("".parse::(), Err(ParseError("Invalid string length"))); - assert_eq!("+".parse::(), Err(ParseError("String has no digits"))); - assert_eq!("-".parse::(), Err(ParseError("String has no digits"))); - - assert_eq!("\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\02".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\02e1".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1e\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("1e1\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("1e1\02".parse::(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("1.0f".parse::(), Err(ParseError("Invalid character in significand"))); - - assert_eq!("..".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("..0".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("1.0.0".parse::(), Err(ParseError("String contains multiple dots"))); -} - -#[test] -fn string_decimal_significand_death() { - assert_eq!(".".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-e".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-e1".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!(".e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.e1".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.e1".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!(".e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.e".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.e".parse::(), Err(ParseError("Significand has no digits"))); -} - -#[test] -fn string_decimal_exponent_death() { - assert_eq!("1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1.e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1.e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!(".1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+.1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-.1e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1.1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1.1e".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1e+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1e-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!(".1e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!(".1e+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!(".1e-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.0e".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1.0e+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1.0e-".parse::(), Err(ParseError("Exponent has no digits"))); -} - -#[test] -fn string_hexadecimal_death() { - assert_eq!("0x".parse::(), Err(ParseError("Invalid string"))); - assert_eq!("+0x".parse::(), Err(ParseError("Invalid string"))); - assert_eq!("-0x".parse::(), Err(ParseError("Invalid string"))); - - assert_eq!("0x0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x0.".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0.".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0.".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x0.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0.0".parse::(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\0".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\02".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\02p1".parse::(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1p\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("0x1p1\0".parse::(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("0x1p1\02".parse::(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("0x1p0f".parse::(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("0x..p1".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("0x..0p1".parse::(), Err(ParseError("String contains multiple dots"))); - assert_eq!("0x1.0.0p1".parse::(), Err(ParseError("String contains multiple dots"))); -} - -#[test] -fn string_hexadecimal_significand_death() { - assert_eq!("0x.".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp+".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp-".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p+".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p+".parse::(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p-".parse::(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p-".parse::(), Err(ParseError("Significand has no digits"))); -} - -#[test] -fn string_hexadecimal_exponent_death() { - assert_eq!("0x1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p+".parse::(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p-".parse::(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p-".parse::(), Err(ParseError("Exponent has no digits"))); -} - -#[test] -fn exact_inverse() { - // Trivial operation. - assert!(Double::from_f64(2.0).get_exact_inverse().unwrap().bitwise_eq(Double::from_f64(0.5))); - assert!(Single::from_f32(2.0).get_exact_inverse().unwrap().bitwise_eq(Single::from_f32(0.5))); - assert!( - "2.0" - .parse::() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::().unwrap()) - ); - assert!( - "2.0" - .parse::() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::().unwrap()) - ); - - // FLT_MIN - assert!( - Single::from_f32(1.17549435e-38) - .get_exact_inverse() - .unwrap() - .bitwise_eq(Single::from_f32(8.5070592e+37)) - ); - - // Large float, inverse is a denormal. - assert!(Single::from_f32(1.7014118e38).get_exact_inverse().is_none()); - // Zero - assert!(Double::from_f64(0.0).get_exact_inverse().is_none()); - // Denormalized float - assert!(Single::from_f32(1.40129846e-45).get_exact_inverse().is_none()); -} - -#[test] -fn round_to_integral() { - let t = Double::from_f64(-0.5); - assert_eq!(-0.0, t.round_to_integral(Round::TowardZero).value.to_f64()); - assert_eq!(-1.0, t.round_to_integral(Round::TowardNegative).value.to_f64()); - assert_eq!(-0.0, t.round_to_integral(Round::TowardPositive).value.to_f64()); - assert_eq!(-0.0, t.round_to_integral(Round::NearestTiesToEven).value.to_f64()); - - let s = Double::from_f64(3.14); - assert_eq!(3.0, s.round_to_integral(Round::TowardZero).value.to_f64()); - assert_eq!(3.0, s.round_to_integral(Round::TowardNegative).value.to_f64()); - assert_eq!(4.0, s.round_to_integral(Round::TowardPositive).value.to_f64()); - assert_eq!(3.0, s.round_to_integral(Round::NearestTiesToEven).value.to_f64()); - - let r = Double::largest(); - assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardZero).value.to_f64()); - assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardNegative).value.to_f64()); - assert_eq!(r.to_f64(), r.round_to_integral(Round::TowardPositive).value.to_f64()); - assert_eq!(r.to_f64(), r.round_to_integral(Round::NearestTiesToEven).value.to_f64()); - - let p = Double::ZERO.round_to_integral(Round::TowardZero).value; - assert_eq!(0.0, p.to_f64()); - let p = (-Double::ZERO).round_to_integral(Round::TowardZero).value; - assert_eq!(-0.0, p.to_f64()); - let p = Double::NAN.round_to_integral(Round::TowardZero).value; - assert!(p.to_f64().is_nan()); - let p = Double::INFINITY.round_to_integral(Round::TowardZero).value; - assert!(p.to_f64().is_infinite() && p.to_f64() > 0.0); - let p = (-Double::INFINITY).round_to_integral(Round::TowardZero).value; - assert!(p.to_f64().is_infinite() && p.to_f64() < 0.0); -} - -#[test] -fn is_integer() { - let t = Double::from_f64(-0.0); - assert!(t.is_integer()); - let t = Double::from_f64(3.14159); - assert!(!t.is_integer()); - let t = Double::NAN; - assert!(!t.is_integer()); - let t = Double::INFINITY; - assert!(!t.is_integer()); - let t = -Double::INFINITY; - assert!(!t.is_integer()); - let t = Double::largest(); - assert!(t.is_integer()); -} - -#[test] -fn largest() { - assert_eq!(3.402823466e+38, Single::largest().to_f32()); - assert_eq!(1.7976931348623158e+308, Double::largest().to_f64()); -} - -#[test] -fn smallest() { - let test = Single::SMALLEST; - let expected = "0x0.000002p-126".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Single::SMALLEST; - let expected = "-0x0.000002p-126".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = Quad::SMALLEST; - let expected = "0x0.0000000000000000000000000001p-16382".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Quad::SMALLEST; - let expected = "-0x0.0000000000000000000000000001p-16382".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(test.is_denormal()); - assert!(test.bitwise_eq(expected)); -} - -#[test] -fn smallest_normalized() { - let test = Single::smallest_normalized(); - let expected = "0x1p-126".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Single::smallest_normalized(); - let expected = "-0x1p-126".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = Quad::smallest_normalized(); - let expected = "0x1p-16382".parse::().unwrap(); - assert!(!test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); - - let test = -Quad::smallest_normalized(); - let expected = "-0x1p-16382".parse::().unwrap(); - assert!(test.is_negative()); - assert!(test.is_finite_non_zero()); - assert!(!test.is_denormal()); - assert!(test.bitwise_eq(expected)); -} - -#[test] -fn zero() { - assert_eq!(0.0, Single::from_f32(0.0).to_f32()); - assert_eq!(-0.0, Single::from_f32(-0.0).to_f32()); - assert!(Single::from_f32(-0.0).is_negative()); - - assert_eq!(0.0, Double::from_f64(0.0).to_f64()); - assert_eq!(-0.0, Double::from_f64(-0.0).to_f64()); - assert!(Double::from_f64(-0.0).is_negative()); - - fn test(sign: bool, bits: u128) { - let test = if sign { -T::ZERO } else { T::ZERO }; - let pattern = if sign { "-0x0p+0" } else { "0x0p+0" }; - let expected = pattern.parse::().unwrap(); - assert!(test.is_zero()); - assert_eq!(sign, test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(bits, test.to_bits()); - } - test::(false, 0); - test::(true, 0x8000); - test::(false, 0); - test::(true, 0x80000000); - test::(false, 0); - test::(true, 0x8000000000000000); - test::(false, 0); - test::(true, 0x8000000000000000_0000000000000000); - test::(false, 0); - test::(true, 0x8000_0000000000000000); -} - -#[test] -fn copy_sign() { - assert!( - Double::from_f64(-42.0) - .bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(-1.0),),) - ); - assert!( - Double::from_f64(42.0) - .bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(1.0),),) - ); - assert!( - Double::from_f64(-42.0) - .bitwise_eq(Double::from_f64(-42.0).copy_sign(Double::from_f64(-1.0),),) - ); - assert!( - Double::from_f64(42.0) - .bitwise_eq(Double::from_f64(42.0).copy_sign(Double::from_f64(1.0),),) - ); -} - -#[test] -fn convert() { - let mut loses_info = false; - let test = "1.0".parse::().unwrap(); - let test: Single = test.convert(&mut loses_info).value; - assert_eq!(1.0, test.to_f32()); - assert!(!loses_info); - - let mut test = "0x1p-53".parse::().unwrap(); - let one = "1.0".parse::().unwrap(); - test += one; - let test: Double = test.convert(&mut loses_info).value; - assert_eq!(1.0, test.to_f64()); - assert!(loses_info); - - let mut test = "0x1p-53".parse::().unwrap(); - let one = "1.0".parse::().unwrap(); - test += one; - let test: Double = test.convert(&mut loses_info).value; - assert_eq!(1.0, test.to_f64()); - assert!(loses_info); - - let test = "0xf.fffffffp+28".parse::().unwrap(); - let test: Double = test.convert(&mut loses_info).value; - assert_eq!(4294967295.0, test.to_f64()); - assert!(!loses_info); - - let test = Single::qnan(None); - let x87_qnan = X87DoubleExtended::qnan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_qnan)); - assert!(!loses_info); - - let test = Single::snan(None); - let sta = test.convert(&mut loses_info); - let test: X87DoubleExtended = sta.value; - assert!(test.is_nan()); - assert!(!test.is_signaling()); - assert!(!loses_info); - assert_eq!(sta.status, Status::INVALID_OP); - - let test = X87DoubleExtended::qnan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_qnan)); - assert!(!loses_info); - - let test = X87DoubleExtended::snan(None); - let sta = test.convert(&mut loses_info); - let test: X87DoubleExtended = sta.value; - assert!(test.is_nan()); - assert!(!test.is_signaling()); - assert!(!loses_info); - assert_eq!(sta.status, Status::INVALID_OP); -} - -#[test] -fn is_negative() { - let t = "0x1p+0".parse::().unwrap(); - assert!(!t.is_negative()); - let t = "-0x1p+0".parse::().unwrap(); - assert!(t.is_negative()); - - assert!(!Single::INFINITY.is_negative()); - assert!((-Single::INFINITY).is_negative()); - - assert!(!Single::ZERO.is_negative()); - assert!((-Single::ZERO).is_negative()); - - assert!(!Single::NAN.is_negative()); - assert!((-Single::NAN).is_negative()); - - assert!(!Single::snan(None).is_negative()); - assert!((-Single::snan(None)).is_negative()); -} - -#[test] -fn is_normal() { - let t = "0x1p+0".parse::().unwrap(); - assert!(t.is_normal()); - - assert!(!Single::INFINITY.is_normal()); - assert!(!Single::ZERO.is_normal()); - assert!(!Single::NAN.is_normal()); - assert!(!Single::snan(None).is_normal()); - assert!(!"0x1p-149".parse::().unwrap().is_normal()); -} - -#[test] -fn is_finite() { - let t = "0x1p+0".parse::().unwrap(); - assert!(t.is_finite()); - assert!(!Single::INFINITY.is_finite()); - assert!(Single::ZERO.is_finite()); - assert!(!Single::NAN.is_finite()); - assert!(!Single::snan(None).is_finite()); - assert!("0x1p-149".parse::().unwrap().is_finite()); -} - -#[test] -fn is_infinite() { - let t = "0x1p+0".parse::().unwrap(); - assert!(!t.is_infinite()); - assert!(Single::INFINITY.is_infinite()); - assert!(!Single::ZERO.is_infinite()); - assert!(!Single::NAN.is_infinite()); - assert!(!Single::snan(None).is_infinite()); - assert!(!"0x1p-149".parse::().unwrap().is_infinite()); -} - -#[test] -fn is_nan() { - let t = "0x1p+0".parse::().unwrap(); - assert!(!t.is_nan()); - assert!(!Single::INFINITY.is_nan()); - assert!(!Single::ZERO.is_nan()); - assert!(Single::NAN.is_nan()); - assert!(Single::snan(None).is_nan()); - assert!(!"0x1p-149".parse::().unwrap().is_nan()); -} - -#[test] -fn is_finite_non_zero() { - // Test positive/negative normal value. - assert!("0x1p+0".parse::().unwrap().is_finite_non_zero()); - assert!("-0x1p+0".parse::().unwrap().is_finite_non_zero()); - - // Test positive/negative denormal value. - assert!("0x1p-149".parse::().unwrap().is_finite_non_zero()); - assert!("-0x1p-149".parse::().unwrap().is_finite_non_zero()); - - // Test +/- Infinity. - assert!(!Single::INFINITY.is_finite_non_zero()); - assert!(!(-Single::INFINITY).is_finite_non_zero()); - - // Test +/- Zero. - assert!(!Single::ZERO.is_finite_non_zero()); - assert!(!(-Single::ZERO).is_finite_non_zero()); - - // Test +/- qNaN. +/- don't mean anything with qNaN but paranoia can't hurt in - // this instance. - assert!(!Single::NAN.is_finite_non_zero()); - assert!(!(-Single::NAN).is_finite_non_zero()); - - // Test +/- sNaN. +/- don't mean anything with sNaN but paranoia can't hurt in - // this instance. - assert!(!Single::snan(None).is_finite_non_zero()); - assert!(!(-Single::snan(None)).is_finite_non_zero()); -} - -#[test] -fn add() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "inf", Status::OK, Category::Infinity), - (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, p_zero, "inf", Status::OK, Category::Infinity), - (p_inf, m_zero, "inf", Status::OK, Category::Infinity), - (p_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, m_inf, "-inf", Status::OK, Category::Infinity), - (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, m_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "inf", Status::OK, Category::Infinity), - (p_zero, m_inf, "-inf", Status::OK, Category::Infinity), - (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (p_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (p_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (p_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_zero, p_inf, "inf", Status::OK, Category::Infinity), - (m_zero, m_inf, "-inf", Status::OK, Category::Infinity), - (m_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (m_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_zero, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_zero, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (m_zero, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (m_zero, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (m_zero, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x1p+1", Status::OK, Category::Normal), - (p_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_inf, "inf", Status::OK, Category::Infinity), - (m_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), - (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, m_normal_value, "-0x1p+1", Status::OK, Category::Normal), - (m_normal_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - p_largest_value, - p_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_largest_value, - m_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_largest_value, p_inf, "inf", Status::OK, Category::Infinity), - (m_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - m_largest_value, - p_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_largest_value, - m_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, p_smallest_value, "0x1p-148", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, p_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, p_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, m_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, m_smallest_value, "-0x1p-148", Status::OK, Category::Normal), - (m_smallest_value, p_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - ( - p_smallest_normalized, - p_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_smallest_normalized, - m_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_normalized, p_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_normalized, "0x1p-125", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_normalized, m_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - ( - m_smallest_normalized, - p_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_smallest_normalized, - m_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_smallest_normalized, p_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, m_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x + y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn subtract() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, m_inf, "inf", Status::OK, Category::Infinity), - (p_inf, p_zero, "inf", Status::OK, Category::Infinity), - (p_inf, m_zero, "inf", Status::OK, Category::Infinity), - (p_inf, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "-inf", Status::OK, Category::Infinity), - (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, m_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "-inf", Status::OK, Category::Infinity), - (p_zero, m_inf, "inf", Status::OK, Category::Infinity), - (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (p_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (p_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (p_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (m_zero, p_inf, "-inf", Status::OK, Category::Infinity), - (m_zero, m_inf, "inf", Status::OK, Category::Infinity), - (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_zero, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_zero, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_zero, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_zero, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (m_zero, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (m_zero, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_zero, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), - (p_normal_value, m_inf, "inf", Status::OK, Category::Infinity), - (p_normal_value, p_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_zero, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, m_normal_value, "0x1p+1", Status::OK, Category::Normal), - (p_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, p_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (p_normal_value, m_smallest_normalized, "0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_normal_value, p_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_zero, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "-0x1p+1", Status::OK, Category::Normal), - (m_normal_value, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, p_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_normal_value, m_smallest_normalized, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (p_largest_value, m_inf, "inf", Status::OK, Category::Infinity), - (p_largest_value, p_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_zero, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_normal_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, p_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_largest_value, m_smallest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - p_largest_value, - p_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_largest_value, - m_smallest_normalized, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_largest_value, p_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_zero, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_normal_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, p_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_largest_value, m_smallest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - ( - m_largest_value, - p_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_largest_value, - m_smallest_normalized, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_value, p_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_zero, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (p_smallest_value, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, m_smallest_value, "0x1p-148", Status::OK, Category::Normal), - (p_smallest_value, p_smallest_normalized, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_normalized, "0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_value, p_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_zero, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_value, p_largest_value, "-0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, m_largest_value, "0x1.fffffep+127", Status::INEXACT, Category::Normal), - (m_smallest_value, p_smallest_value, "-0x1p-148", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, p_smallest_normalized, "-0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_normalized, "0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_normalized, p_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_zero, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (p_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - ( - p_smallest_normalized, - p_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - p_smallest_normalized, - m_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (p_smallest_normalized, p_smallest_value, "0x1.fffffcp-127", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_value, "0x1.000002p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, m_smallest_normalized, "0x1p-125", Status::OK, Category::Normal), - (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_normalized, p_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_zero, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, qnan, "-nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "-nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "-0x1p+0", Status::INEXACT, Category::Normal), - (m_smallest_normalized, m_normal_value, "0x1p+0", Status::INEXACT, Category::Normal), - ( - m_smallest_normalized, - p_largest_value, - "-0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - ( - m_smallest_normalized, - m_largest_value, - "0x1.fffffep+127", - Status::INEXACT, - Category::Normal, - ), - (m_smallest_normalized, p_smallest_value, "-0x1.000002p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_value, "-0x1.fffffcp-127", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_normalized, "-0x1p-125", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x - y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn multiply() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - let underflow_status = Status::UNDERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "inf", Status::OK, Category::Infinity), - (p_inf, m_inf, "-inf", Status::OK, Category::Infinity), - (p_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "-inf", Status::OK, Category::Infinity), - (m_inf, m_inf, "inf", Status::OK, Category::Infinity), - (m_inf, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_normal_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_normal_value, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_normal_value, p_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_normal_value, m_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_normal_value, p_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (p_normal_value, m_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (p_normal_value, p_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (p_normal_value, m_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_normal_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_normal_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_normal_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_normal_value, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_normal_value, p_largest_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_normal_value, m_largest_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_normal_value, p_smallest_value, "-0x1p-149", Status::OK, Category::Normal), - (m_normal_value, m_smallest_value, "0x1p-149", Status::OK, Category::Normal), - (m_normal_value, p_smallest_normalized, "-0x1p-126", Status::OK, Category::Normal), - (m_normal_value, m_smallest_normalized, "0x1p-126", Status::OK, Category::Normal), - (p_largest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_largest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_largest_value, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, p_largest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_largest_value, "-inf", overflow_status, Category::Infinity), - (p_largest_value, p_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (p_largest_value, m_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (p_largest_value, p_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal), - (p_largest_value, m_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal), - (m_largest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_largest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_largest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_largest_value, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, p_largest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_largest_value, "inf", overflow_status, Category::Infinity), - (m_largest_value, p_smallest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (m_largest_value, m_smallest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (m_largest_value, p_smallest_normalized, "-0x1.fffffep+1", Status::OK, Category::Normal), - (m_largest_value, m_smallest_normalized, "0x1.fffffep+1", Status::OK, Category::Normal), - (p_smallest_value, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_value, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_value, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, p_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (p_smallest_value, m_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (p_smallest_value, p_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_value, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_value, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, p_largest_value, "-0x1.fffffep-22", Status::OK, Category::Normal), - (m_smallest_value, m_largest_value, "0x1.fffffep-22", Status::OK, Category::Normal), - (m_smallest_value, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, m_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, p_inf, "inf", Status::OK, Category::Infinity), - (p_smallest_normalized, m_inf, "-inf", Status::OK, Category::Infinity), - (p_smallest_normalized, p_zero, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, m_zero, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal), - (p_smallest_normalized, m_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, m_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, p_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, m_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, p_inf, "-inf", Status::OK, Category::Infinity), - (m_smallest_normalized, m_inf, "inf", Status::OK, Category::Infinity), - (m_smallest_normalized, p_zero, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, m_zero, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, p_largest_value, "-0x1.fffffep+1", Status::OK, Category::Normal), - (m_smallest_normalized, m_largest_value, "0x1.fffffep+1", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, m_smallest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, p_smallest_normalized, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, m_smallest_normalized, "0x0p+0", underflow_status, Category::Zero), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x * y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn divide() { - // Test Special Cases against each other and normal values. - - // FIXMES/NOTES: - // 1. Since we perform only default exception handling all operations with - // signaling NaNs should have a result that is a quiet NaN. Currently they - // return sNaN. - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let qnan = Single::NAN; - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - let overflow_status = Status::OVERFLOW | Status::INEXACT; - let underflow_status = Status::UNDERFLOW | Status::INEXACT; - - let special_cases = [ - (p_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (p_inf, p_zero, "inf", Status::OK, Category::Infinity), - (p_inf, m_zero, "-inf", Status::OK, Category::Infinity), - (p_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_inf, p_normal_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_normal_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_largest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_largest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_value, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_value, "-inf", Status::OK, Category::Infinity), - (p_inf, p_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_inf, m_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (m_inf, p_zero, "-inf", Status::OK, Category::Infinity), - (m_inf, m_zero, "inf", Status::OK, Category::Infinity), - (m_inf, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_inf, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_inf, p_normal_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_normal_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_largest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_largest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_value, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_value, "inf", Status::OK, Category::Infinity), - (m_inf, p_smallest_normalized, "-inf", Status::OK, Category::Infinity), - (m_inf, m_smallest_normalized, "inf", Status::OK, Category::Infinity), - (p_zero, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (p_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_zero, p_normal_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_largest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (p_zero, p_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (p_zero, m_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (m_zero, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_zero, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_zero, p_normal_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_normal_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_largest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_largest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_value, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_value, "0x0p+0", Status::OK, Category::Zero), - (m_zero, p_smallest_normalized, "-0x0p+0", Status::OK, Category::Zero), - (m_zero, m_smallest_normalized, "0x0p+0", Status::OK, Category::Zero), - (qnan, p_inf, "nan", Status::OK, Category::NaN), - (qnan, m_inf, "nan", Status::OK, Category::NaN), - (qnan, p_zero, "nan", Status::OK, Category::NaN), - (qnan, m_zero, "nan", Status::OK, Category::NaN), - (qnan, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (qnan, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (qnan, p_normal_value, "nan", Status::OK, Category::NaN), - (qnan, m_normal_value, "nan", Status::OK, Category::NaN), - (qnan, p_largest_value, "nan", Status::OK, Category::NaN), - (qnan, m_largest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_value, "nan", Status::OK, Category::NaN), - (qnan, p_smallest_normalized, "nan", Status::OK, Category::NaN), - (qnan, m_smallest_normalized, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (snan, p_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_inf, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_zero, "nan", Status::INVALID_OP, Category::NaN), - (snan, qnan, "nan", Status::INVALID_OP, Category::NaN), - (snan, snan, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_normal_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_largest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_value, "nan", Status::INVALID_OP, Category::NaN), - (snan, p_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - (snan, m_smallest_normalized, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_normal_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_normal_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_normal_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_normal_value, p_normal_value, "0x1p+0", Status::OK, Category::Normal), - (p_normal_value, m_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (p_normal_value, p_largest_value, "0x1p-128", underflow_status, Category::Normal), - (p_normal_value, m_largest_value, "-0x1p-128", underflow_status, Category::Normal), - (p_normal_value, p_smallest_value, "inf", overflow_status, Category::Infinity), - (p_normal_value, m_smallest_value, "-inf", overflow_status, Category::Infinity), - (p_normal_value, p_smallest_normalized, "0x1p+126", Status::OK, Category::Normal), - (p_normal_value, m_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal), - (m_normal_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_normal_value, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_normal_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_normal_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_normal_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_normal_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_normal_value, p_normal_value, "-0x1p+0", Status::OK, Category::Normal), - (m_normal_value, m_normal_value, "0x1p+0", Status::OK, Category::Normal), - (m_normal_value, p_largest_value, "-0x1p-128", underflow_status, Category::Normal), - (m_normal_value, m_largest_value, "0x1p-128", underflow_status, Category::Normal), - (m_normal_value, p_smallest_value, "-inf", overflow_status, Category::Infinity), - (m_normal_value, m_smallest_value, "inf", overflow_status, Category::Infinity), - (m_normal_value, p_smallest_normalized, "-0x1p+126", Status::OK, Category::Normal), - (m_normal_value, m_smallest_normalized, "0x1p+126", Status::OK, Category::Normal), - (p_largest_value, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_largest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_largest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_largest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_largest_value, p_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, m_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (p_largest_value, p_largest_value, "0x1p+0", Status::OK, Category::Normal), - (p_largest_value, m_largest_value, "-0x1p+0", Status::OK, Category::Normal), - (p_largest_value, p_smallest_value, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_smallest_value, "-inf", overflow_status, Category::Infinity), - (p_largest_value, p_smallest_normalized, "inf", overflow_status, Category::Infinity), - (p_largest_value, m_smallest_normalized, "-inf", overflow_status, Category::Infinity), - (m_largest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_largest_value, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_largest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_largest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_largest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_largest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_largest_value, p_normal_value, "-0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, m_normal_value, "0x1.fffffep+127", Status::OK, Category::Normal), - (m_largest_value, p_largest_value, "-0x1p+0", Status::OK, Category::Normal), - (m_largest_value, m_largest_value, "0x1p+0", Status::OK, Category::Normal), - (m_largest_value, p_smallest_value, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_smallest_value, "inf", overflow_status, Category::Infinity), - (m_largest_value, p_smallest_normalized, "-inf", overflow_status, Category::Infinity), - (m_largest_value, m_smallest_normalized, "inf", overflow_status, Category::Infinity), - (p_smallest_value, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_value, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_value, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_value, p_normal_value, "0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, m_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (p_smallest_value, p_largest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, m_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_value, p_smallest_value, "0x1p+0", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_value, "-0x1p+0", Status::OK, Category::Normal), - (p_smallest_value, p_smallest_normalized, "0x1p-23", Status::OK, Category::Normal), - (p_smallest_value, m_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal), - (m_smallest_value, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_value, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_value, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_value, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_value, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_value, p_normal_value, "-0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, m_normal_value, "0x1p-149", Status::OK, Category::Normal), - (m_smallest_value, p_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, m_largest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_value, p_smallest_value, "-0x1p+0", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_value, "0x1p+0", Status::OK, Category::Normal), - (m_smallest_value, p_smallest_normalized, "-0x1p-23", Status::OK, Category::Normal), - (m_smallest_value, m_smallest_normalized, "0x1p-23", Status::OK, Category::Normal), - (p_smallest_normalized, p_inf, "0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, m_inf, "-0x0p+0", Status::OK, Category::Zero), - (p_smallest_normalized, p_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_normalized, m_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (p_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (p_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (p_smallest_normalized, p_normal_value, "0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, m_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (p_smallest_normalized, p_largest_value, "0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, m_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (p_smallest_normalized, p_smallest_value, "0x1p+23", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_value, "-0x1p+23", Status::OK, Category::Normal), - (p_smallest_normalized, p_smallest_normalized, "0x1p+0", Status::OK, Category::Normal), - (p_smallest_normalized, m_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal), - (m_smallest_normalized, p_inf, "-0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, m_inf, "0x0p+0", Status::OK, Category::Zero), - (m_smallest_normalized, p_zero, "-inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_normalized, m_zero, "inf", Status::DIV_BY_ZERO, Category::Infinity), - (m_smallest_normalized, qnan, "nan", Status::OK, Category::NaN), - /* - // See Note 1. - (m_smallest_normalized, snan, "nan", Status::INVALID_OP, Category::NaN), - */ - (m_smallest_normalized, p_normal_value, "-0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, m_normal_value, "0x1p-126", Status::OK, Category::Normal), - (m_smallest_normalized, p_largest_value, "-0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, m_largest_value, "0x0p+0", underflow_status, Category::Zero), - (m_smallest_normalized, p_smallest_value, "-0x1p+23", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_value, "0x1p+23", Status::OK, Category::Normal), - (m_smallest_normalized, p_smallest_normalized, "-0x1p+0", Status::OK, Category::Normal), - (m_smallest_normalized, m_smallest_normalized, "0x1p+0", Status::OK, Category::Normal), - ]; - - for (x, y, e_result, e_status, e_category) in special_cases { - let status; - let result = unpack!(status=, x / y); - assert_eq!(status, e_status); - assert_eq!(result.category(), e_category); - assert!(result.bitwise_eq(e_result.parse::().unwrap())); - } -} - -#[test] -fn operator_overloads() { - // This is mostly testing that these operator overloads compile. - let one = "0x1p+0".parse::().unwrap(); - let two = "0x2p+0".parse::().unwrap(); - assert!(two.bitwise_eq((one + one).value)); - assert!(one.bitwise_eq((two - one).value)); - assert!(two.bitwise_eq((one * two).value)); - assert!(one.bitwise_eq((two / two).value)); -} - -#[test] -fn abs() { - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let p_qnan = Single::NAN; - let m_qnan = -Single::NAN; - let p_snan = Single::snan(None); - let m_snan = -Single::snan(None); - let p_normal_value = "0x1p+0".parse::().unwrap(); - let m_normal_value = "-0x1p+0".parse::().unwrap(); - let p_largest_value = Single::largest(); - let m_largest_value = -Single::largest(); - let p_smallest_value = Single::SMALLEST; - let m_smallest_value = -Single::SMALLEST; - let p_smallest_normalized = Single::smallest_normalized(); - let m_smallest_normalized = -Single::smallest_normalized(); - - assert!(p_inf.bitwise_eq(p_inf.abs())); - assert!(p_inf.bitwise_eq(m_inf.abs())); - assert!(p_zero.bitwise_eq(p_zero.abs())); - assert!(p_zero.bitwise_eq(m_zero.abs())); - assert!(p_qnan.bitwise_eq(p_qnan.abs())); - assert!(p_qnan.bitwise_eq(m_qnan.abs())); - assert!(p_snan.bitwise_eq(p_snan.abs())); - assert!(p_snan.bitwise_eq(m_snan.abs())); - assert!(p_normal_value.bitwise_eq(p_normal_value.abs())); - assert!(p_normal_value.bitwise_eq(m_normal_value.abs())); - assert!(p_largest_value.bitwise_eq(p_largest_value.abs())); - assert!(p_largest_value.bitwise_eq(m_largest_value.abs())); - assert!(p_smallest_value.bitwise_eq(p_smallest_value.abs())); - assert!(p_smallest_value.bitwise_eq(m_smallest_value.abs())); - assert!(p_smallest_normalized.bitwise_eq(p_smallest_normalized.abs(),)); - assert!(p_smallest_normalized.bitwise_eq(m_smallest_normalized.abs(),)); -} - -#[test] -fn neg() { - let one = "1.0".parse::().unwrap(); - let neg_one = "-1.0".parse::().unwrap(); - let zero = Single::ZERO; - let neg_zero = -Single::ZERO; - let inf = Single::INFINITY; - let neg_inf = -Single::INFINITY; - let qnan = Single::NAN; - let neg_qnan = -Single::NAN; - - assert!(neg_one.bitwise_eq(-one)); - assert!(one.bitwise_eq(-neg_one)); - assert!(neg_zero.bitwise_eq(-zero)); - assert!(zero.bitwise_eq(-neg_zero)); - assert!(neg_inf.bitwise_eq(-inf)); - assert!(inf.bitwise_eq(-neg_inf)); - assert!(neg_inf.bitwise_eq(-inf)); - assert!(inf.bitwise_eq(-neg_inf)); - assert!(neg_qnan.bitwise_eq(-qnan)); - assert!(qnan.bitwise_eq(-neg_qnan)); -} - -#[test] -fn ilogb() { - assert_eq!(-1074, Double::SMALLEST.ilogb()); - assert_eq!(-1074, (-Double::SMALLEST).ilogb()); - assert_eq!(-1023, "0x1.ffffffffffffep-1024".parse::().unwrap().ilogb()); - assert_eq!(-1023, "0x1.ffffffffffffep-1023".parse::().unwrap().ilogb()); - assert_eq!(-1023, "-0x1.ffffffffffffep-1023".parse::().unwrap().ilogb()); - assert_eq!(-51, "0x1p-51".parse::().unwrap().ilogb()); - assert_eq!(-1023, "0x1.c60f120d9f87cp-1023".parse::().unwrap().ilogb()); - assert_eq!(-2, "0x0.ffffp-1".parse::().unwrap().ilogb()); - assert_eq!(-1023, "0x1.fffep-1023".parse::().unwrap().ilogb()); - assert_eq!(1023, Double::largest().ilogb()); - assert_eq!(1023, (-Double::largest()).ilogb()); - - assert_eq!(0, "0x1p+0".parse::().unwrap().ilogb()); - assert_eq!(0, "-0x1p+0".parse::().unwrap().ilogb()); - assert_eq!(42, "0x1p+42".parse::().unwrap().ilogb()); - assert_eq!(-42, "0x1p-42".parse::().unwrap().ilogb()); - - assert_eq!(IEK_INF, Single::INFINITY.ilogb()); - assert_eq!(IEK_INF, (-Single::INFINITY).ilogb()); - assert_eq!(IEK_ZERO, Single::ZERO.ilogb()); - assert_eq!(IEK_ZERO, (-Single::ZERO).ilogb()); - assert_eq!(IEK_NAN, Single::NAN.ilogb()); - assert_eq!(IEK_NAN, Single::snan(None).ilogb()); - - assert_eq!(127, Single::largest().ilogb()); - assert_eq!(127, (-Single::largest()).ilogb()); - - assert_eq!(-149, Single::SMALLEST.ilogb()); - assert_eq!(-149, (-Single::SMALLEST).ilogb()); - assert_eq!(-126, Single::smallest_normalized().ilogb()); - assert_eq!(-126, (-Single::smallest_normalized()).ilogb()); -} - -#[test] -fn scalbn() { - assert!( - "0x1p+0" - .parse::() - .unwrap() - .bitwise_eq("0x1p+0".parse::().unwrap().scalbn(0),) - ); - assert!( - "0x1p+42" - .parse::() - .unwrap() - .bitwise_eq("0x1p+0".parse::().unwrap().scalbn(42),) - ); - assert!( - "0x1p-42" - .parse::() - .unwrap() - .bitwise_eq("0x1p+0".parse::().unwrap().scalbn(-42),) - ); - - let p_inf = Single::INFINITY; - let m_inf = -Single::INFINITY; - let p_zero = Single::ZERO; - let m_zero = -Single::ZERO; - let p_qnan = Single::NAN; - let m_qnan = -Single::NAN; - let snan = Single::snan(None); - - assert!(p_inf.bitwise_eq(p_inf.scalbn(0))); - assert!(m_inf.bitwise_eq(m_inf.scalbn(0))); - assert!(p_zero.bitwise_eq(p_zero.scalbn(0))); - assert!(m_zero.bitwise_eq(m_zero.scalbn(0))); - assert!(p_qnan.bitwise_eq(p_qnan.scalbn(0))); - assert!(m_qnan.bitwise_eq(m_qnan.scalbn(0))); - assert!(!snan.scalbn(0).is_signaling()); - - let scalbn_snan = snan.scalbn(1); - assert!(scalbn_snan.is_nan() && !scalbn_snan.is_signaling()); - - // Make sure highest bit of payload is preserved. - let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1; - - let snan_with_payload = Double::snan(Some(payload)); - let quiet_payload = snan_with_payload.scalbn(1); - assert!(quiet_payload.is_nan() && !quiet_payload.is_signaling()); - assert_eq!(payload, quiet_payload.to_bits() & ((1 << 51) - 1)); - - assert!(p_inf.bitwise_eq("0x1p+0".parse::().unwrap().scalbn(128),)); - assert!(m_inf.bitwise_eq("-0x1p+0".parse::().unwrap().scalbn(128),)); - assert!(p_inf.bitwise_eq("0x1p+127".parse::().unwrap().scalbn(1),)); - assert!(p_zero.bitwise_eq("0x1p-127".parse::().unwrap().scalbn(-127),)); - assert!(m_zero.bitwise_eq("-0x1p-127".parse::().unwrap().scalbn(-127),)); - assert!( - "-0x1p-149" - .parse::() - .unwrap() - .bitwise_eq("-0x1p-127".parse::().unwrap().scalbn(-22),) - ); - assert!(p_zero.bitwise_eq("0x1p-126".parse::().unwrap().scalbn(-24),)); - - let smallest_f64 = Double::SMALLEST; - let neg_smallest_f64 = -Double::SMALLEST; - - let largest_f64 = Double::largest(); - let neg_largest_f64 = -Double::largest(); - - let largest_denormal_f64 = "0x1.ffffffffffffep-1023".parse::().unwrap(); - let neg_largest_denormal_f64 = "-0x1.ffffffffffffep-1023".parse::().unwrap(); - - assert!(smallest_f64.bitwise_eq("0x1p-1074".parse::().unwrap().scalbn(0),)); - assert!(neg_smallest_f64.bitwise_eq("-0x1p-1074".parse::().unwrap().scalbn(0),)); - - assert!("0x1p+1023".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2097,),)); - - assert!(smallest_f64.scalbn(-2097).is_pos_zero()); - assert!(smallest_f64.scalbn(-2098).is_pos_zero()); - assert!(smallest_f64.scalbn(-2099).is_pos_zero()); - assert!("0x1p+1022".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2096,),)); - assert!("0x1p+1023".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2097,),)); - assert!(smallest_f64.scalbn(2098).is_infinite()); - assert!(smallest_f64.scalbn(2099).is_infinite()); - - // Test for integer overflows when adding to exponent. - assert!(smallest_f64.scalbn(-ExpInt::MAX).is_pos_zero()); - assert!(largest_f64.scalbn(ExpInt::MAX).is_infinite()); - - assert!(largest_denormal_f64.bitwise_eq(largest_denormal_f64.scalbn(0),)); - assert!(neg_largest_denormal_f64.bitwise_eq(neg_largest_denormal_f64.scalbn(0),)); - - assert!( - "0x1.ffffffffffffep-1022" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1)) - ); - assert!( - "-0x1.ffffffffffffep-1021" - .parse::() - .unwrap() - .bitwise_eq(neg_largest_denormal_f64.scalbn(2)) - ); - - assert!( - "0x1.ffffffffffffep+1" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1024)) - ); - assert!(largest_denormal_f64.scalbn(-1023).is_pos_zero()); - assert!(largest_denormal_f64.scalbn(-1024).is_pos_zero()); - assert!(largest_denormal_f64.scalbn(-2048).is_pos_zero()); - assert!(largest_denormal_f64.scalbn(2047).is_infinite()); - assert!(largest_denormal_f64.scalbn(2098).is_infinite()); - assert!(largest_denormal_f64.scalbn(2099).is_infinite()); - - assert!( - "0x1.ffffffffffffep-2" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1021)) - ); - assert!( - "0x1.ffffffffffffep-1" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1022)) - ); - assert!( - "0x1.ffffffffffffep+0" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1023)) - ); - assert!( - "0x1.ffffffffffffep+1023" - .parse::() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(2046)) - ); - assert!("0x1p+974".parse::().unwrap().bitwise_eq(smallest_f64.scalbn(2048,),)); - - let random_denormal_f64 = "0x1.c60f120d9f87cp+51".parse::().unwrap(); - assert!( - "0x1.c60f120d9f87cp-972" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-1023)) - ); - assert!( - "0x1.c60f120d9f87cp-1" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-52)) - ); - assert!( - "0x1.c60f120d9f87cp-2" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-53)) - ); - assert!( - "0x1.c60f120d9f87cp+0" - .parse::() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-51)) - ); - - assert!(random_denormal_f64.scalbn(-2097).is_pos_zero()); - assert!(random_denormal_f64.scalbn(-2090).is_pos_zero()); - - assert!("-0x1p-1073".parse::().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2097),)); - - assert!("-0x1p-1024".parse::().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2048),)); - - assert!("0x1p-1073".parse::().unwrap().bitwise_eq(largest_f64.scalbn(-2097,),)); - - assert!("0x1p-1074".parse::().unwrap().bitwise_eq(largest_f64.scalbn(-2098,),)); - assert!("-0x1p-1074".parse::().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2098),)); - assert!(neg_largest_f64.scalbn(-2099).is_neg_zero()); - assert!(largest_f64.scalbn(1).is_infinite()); - - assert!( - "0x1p+0" - .parse::() - .unwrap() - .bitwise_eq("0x1p+52".parse::().unwrap().scalbn(-52),) - ); - - assert!( - "0x1p-103" - .parse::() - .unwrap() - .bitwise_eq("0x1p-51".parse::().unwrap().scalbn(-52),) - ); -} - -#[test] -fn frexp() { - let p_zero = Double::ZERO; - let m_zero = -Double::ZERO; - let one = Double::from_f64(1.0); - let m_one = Double::from_f64(-1.0); - - let largest_denormal = "0x1.ffffffffffffep-1023".parse::().unwrap(); - let neg_largest_denormal = "-0x1.ffffffffffffep-1023".parse::().unwrap(); - - let smallest = Double::SMALLEST; - let neg_smallest = -Double::SMALLEST; - - let largest = Double::largest(); - let neg_largest = -Double::largest(); - - let p_inf = Double::INFINITY; - let m_inf = -Double::INFINITY; - - let p_qnan = Double::NAN; - let m_qnan = -Double::NAN; - let snan = Double::snan(None); - - // Make sure highest bit of payload is preserved. - let payload = (1 << 50) | (1 << 49) | (1234 << 32) | 1; - - let snan_with_payload = Double::snan(Some(payload)); - - let mut exp = 0; - - let frac = p_zero.frexp(&mut exp); - assert_eq!(0, exp); - assert!(frac.is_pos_zero()); - - let frac = m_zero.frexp(&mut exp); - assert_eq!(0, exp); - assert!(frac.is_neg_zero()); - - let frac = one.frexp(&mut exp); - assert_eq!(1, exp); - assert!("0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = m_one.frexp(&mut exp); - assert_eq!(1, exp); - assert!("-0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = largest_denormal.frexp(&mut exp); - assert_eq!(-1022, exp); - assert!("0x1.ffffffffffffep-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = neg_largest_denormal.frexp(&mut exp); - assert_eq!(-1022, exp); - assert!("-0x1.ffffffffffffep-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = smallest.frexp(&mut exp); - assert_eq!(-1073, exp); - assert!("0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = neg_smallest.frexp(&mut exp); - assert_eq!(-1073, exp); - assert!("-0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = largest.frexp(&mut exp); - assert_eq!(1024, exp); - assert!("0x1.fffffffffffffp-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = neg_largest.frexp(&mut exp); - assert_eq!(1024, exp); - assert!("-0x1.fffffffffffffp-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = p_inf.frexp(&mut exp); - assert_eq!(IEK_INF, exp); - assert!(frac.is_infinite() && !frac.is_negative()); - - let frac = m_inf.frexp(&mut exp); - assert_eq!(IEK_INF, exp); - assert!(frac.is_infinite() && frac.is_negative()); - - let frac = p_qnan.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan()); - - let frac = m_qnan.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan()); - - let frac = snan.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan() && !frac.is_signaling()); - - let frac = snan_with_payload.frexp(&mut exp); - assert_eq!(IEK_NAN, exp); - assert!(frac.is_nan() && !frac.is_signaling()); - assert_eq!(payload, frac.to_bits() & ((1 << 51) - 1)); - - let frac = "0x0.ffffp-1".parse::().unwrap().frexp(&mut exp); - assert_eq!(-1, exp); - assert!("0x1.fffep-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = "0x1p-51".parse::().unwrap().frexp(&mut exp); - assert_eq!(-50, exp); - assert!("0x1p-1".parse::().unwrap().bitwise_eq(frac)); - - let frac = "0x1.c60f120d9f87cp+51".parse::().unwrap().frexp(&mut exp); - assert_eq!(52, exp); - assert!("0x1.c60f120d9f87cp-1".parse::().unwrap().bitwise_eq(frac)); -} - -#[test] -fn modulo() { - let mut status; - { - let f1 = "1.5".parse::().unwrap(); - let f2 = "1.0".parse::().unwrap(); - let expected = "0.5".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0.5".parse::().unwrap(); - let f2 = "1.0".parse::().unwrap(); - let expected = "0.5".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1.3333333333333p-2".parse::().unwrap(); // 0.3 - let f2 = "0x1.47ae147ae147bp-7".parse::().unwrap(); // 0.01 - // 0.009999999999999983 - let expected = "0x1.47ae147ae1471p-7".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1p64".parse::().unwrap(); // 1.8446744073709552e19 - let f2 = "1.5".parse::().unwrap(); - let expected = "1.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1p1000".parse::().unwrap(); - let f2 = "0x1p-1000".parse::().unwrap(); - let expected = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0.0".parse::().unwrap(); - let f2 = "1.0".parse::().unwrap(); - let expected = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "1.0".parse::().unwrap(); - let f2 = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } - { - let f1 = "0.0".parse::().unwrap(); - let f2 = "0.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } - { - let f1 = Double::INFINITY; - let f2 = "1.0".parse::().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } -} diff --git a/compiler/rustc_apfloat/tests/ppc.rs b/compiler/rustc_apfloat/tests/ppc.rs deleted file mode 100644 index c769d2654378..000000000000 --- a/compiler/rustc_apfloat/tests/ppc.rs +++ /dev/null @@ -1,530 +0,0 @@ -use rustc_apfloat::ppc::DoubleDouble; -use rustc_apfloat::{Category, Float, Round}; - -use std::cmp::Ordering; - -#[test] -fn ppc_double_double() { - let test = DoubleDouble::ZERO; - let expected = "0x0p+0".parse::().unwrap(); - assert!(test.is_zero()); - assert!(!test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(0, test.to_bits()); - - let test = -DoubleDouble::ZERO; - let expected = "-0x0p+0".parse::().unwrap(); - assert!(test.is_zero()); - assert!(test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(0x8000000000000000, test.to_bits()); - - let test = "1.0".parse::().unwrap(); - assert_eq!(0x3ff0000000000000, test.to_bits()); - - // LDBL_MAX - let test = "1.79769313486231580793728971405301e+308".parse::().unwrap(); - assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, test.to_bits()); - - // LDBL_MIN - let test = "2.00416836000897277799610805135016e-292".parse::().unwrap(); - assert_eq!(0x0000000000000000_0360000000000000, test.to_bits()); -} - -#[test] -fn ppc_double_double_add_special() { - let data = [ - // (1 + 0) + (-1 + 0) = Category::Zero - (0x3ff0000000000000, 0xbff0000000000000, Category::Zero, Round::NearestTiesToEven), - // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x7948000000000000, - Category::Infinity, - Round::NearestTiesToEven, - ), - // FIXME: change the 4th 0x75effffffffffffe to 0x75efffffffffffff when - // DoubleDouble's fallback is gone. - // LDBL_MAX + (1.011111... >> (1023 - 106) + (1.1111111...0 >> (1023 - - // 160))) = Category::Normal - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x75effffffffffffe_7947ffffffffffff, - Category::Normal, - Round::NearestTiesToEven, - ), - // LDBL_MAX + (1.1 >> (1023 - 106) + 0)) = Category::Infinity - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x7c8ffffffffffffe_7fefffffffffffff, - Category::Infinity, - Round::NearestTiesToEven, - ), - // NaN + (1 + 0) = Category::NaN - (0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.add_r(a2, round).value; - - assert_eq!(expected, a1.category(), "{:#x} + {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.add_r(a1, round).value; - - assert_eq!(expected, a2.category(), "{:#x} + {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_add() { - let data = [ - // (1 + 0) + (1e-105 + 0) = (1 + 1e-105) - ( - 0x3ff0000000000000, - 0x3960000000000000, - 0x3960000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 0) + (1e-106 + 0) = (1 + 1e-106) - ( - 0x3ff0000000000000, - 0x3950000000000000, - 0x3950000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 1e-106) + (1e-106 + 0) = (1 + 1e-105) - ( - 0x3950000000000000_3ff0000000000000, - 0x3950000000000000, - 0x3960000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 0) + (epsilon + 0) = (1 + epsilon) - ( - 0x3ff0000000000000, - 0x0000000000000001, - 0x0000000000000001_3ff0000000000000, - Round::NearestTiesToEven, - ), - // FIXME: change 0xf950000000000000 to 0xf940000000000000, when - // DoubleDouble's fallback is gone. - // (DBL_MAX - 1 << (1023 - 105)) + (1 << (1023 - 53) + 0) = DBL_MAX + - // 1.11111... << (1023 - 52) - ( - 0xf950000000000000_7fefffffffffffff, - 0x7c90000000000000, - 0x7c8ffffffffffffe_7fefffffffffffff, - Round::NearestTiesToEven, - ), - // FIXME: change 0xf950000000000000 to 0xf940000000000000, when - // DoubleDouble's fallback is gone. - // (1 << (1023 - 53) + 0) + (DBL_MAX - 1 << (1023 - 105)) = DBL_MAX + - // 1.11111... << (1023 - 52) - ( - 0x7c90000000000000, - 0xf950000000000000_7fefffffffffffff, - 0x7c8ffffffffffffe_7fefffffffffffff, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.add_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} + {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.add_r(a1, round).value; - - assert_eq!(expected, a2.to_bits(), "{:#x} + {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_subtract() { - let data = [ - // (1 + 0) - (-1e-105 + 0) = (1 + 1e-105) - ( - 0x3ff0000000000000, - 0xb960000000000000, - 0x3960000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + 0) - (-1e-106 + 0) = (1 + 1e-106) - ( - 0x3ff0000000000000, - 0xb950000000000000, - 0x3950000000000000_3ff0000000000000, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.sub_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} - {:#x}", op1, op2); - } -} - -#[test] -fn ppc_double_double_multiply_special() { - let data = [ - // Category::NaN * Category::NaN = Category::NaN - (0x7ff8000000000000, 0x7ff8000000000000, Category::NaN, Round::NearestTiesToEven), - // Category::NaN * Category::Zero = Category::NaN - (0x7ff8000000000000, 0, Category::NaN, Round::NearestTiesToEven), - // Category::NaN * Category::Infinity = Category::NaN - (0x7ff8000000000000, 0x7ff0000000000000, Category::NaN, Round::NearestTiesToEven), - // Category::NaN * Category::Normal = Category::NaN - (0x7ff8000000000000, 0x3ff0000000000000, Category::NaN, Round::NearestTiesToEven), - // Category::Infinity * Category::Infinity = Category::Infinity - (0x7ff0000000000000, 0x7ff0000000000000, Category::Infinity, Round::NearestTiesToEven), - // Category::Infinity * Category::Zero = Category::NaN - (0x7ff0000000000000, 0, Category::NaN, Round::NearestTiesToEven), - // Category::Infinity * Category::Normal = Category::Infinity - (0x7ff0000000000000, 0x3ff0000000000000, Category::Infinity, Round::NearestTiesToEven), - // Category::Zero * Category::Zero = Category::Zero - (0, 0, Category::Zero, Round::NearestTiesToEven), - // Category::Zero * Category::Normal = Category::Zero - (0, 0x3ff0000000000000, Category::Zero, Round::NearestTiesToEven), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.mul_r(a2, round).value; - - assert_eq!(expected, a1.category(), "{:#x} * {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.mul_r(a1, round).value; - - assert_eq!(expected, a2.category(), "{:#x} * {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_multiply() { - let data = [ - // 1/3 * 3 = 1.0 - ( - 0x3c75555555555556_3fd5555555555555, - 0x4008000000000000, - 0x3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + epsilon) * (1 + 0) = Category::Zero - ( - 0x0000000000000001_3ff0000000000000, - 0x3ff0000000000000, - 0x0000000000000001_3ff0000000000000, - Round::NearestTiesToEven, - ), - // (1 + epsilon) * (1 + epsilon) = 1 + 2 * epsilon - ( - 0x0000000000000001_3ff0000000000000, - 0x0000000000000001_3ff0000000000000, - 0x0000000000000002_3ff0000000000000, - Round::NearestTiesToEven, - ), - // -(1 + epsilon) * (1 + epsilon) = -1 - ( - 0x0000000000000001_bff0000000000000, - 0x0000000000000001_3ff0000000000000, - 0xbff0000000000000, - Round::NearestTiesToEven, - ), - // (0.5 + 0) * (1 + 2 * epsilon) = 0.5 + epsilon - ( - 0x3fe0000000000000, - 0x0000000000000002_3ff0000000000000, - 0x0000000000000001_3fe0000000000000, - Round::NearestTiesToEven, - ), - // (0.5 + 0) * (1 + epsilon) = 0.5 - ( - 0x3fe0000000000000, - 0x0000000000000001_3ff0000000000000, - 0x3fe0000000000000, - Round::NearestTiesToEven, - ), - // __LDBL_MAX__ * (1 + 1 << 106) = inf - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x3950000000000000_3ff0000000000000, - 0x7ff0000000000000, - Round::NearestTiesToEven, - ), - // __LDBL_MAX__ * (1 + 1 << 107) > __LDBL_MAX__, but not inf, yes =_=||| - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x3940000000000000_3ff0000000000000, - 0x7c8fffffffffffff_7fefffffffffffff, - Round::NearestTiesToEven, - ), - // __LDBL_MAX__ * (1 + 1 << 108) = __LDBL_MAX__ - ( - 0x7c8ffffffffffffe_7fefffffffffffff, - 0x3930000000000000_3ff0000000000000, - 0x7c8ffffffffffffe_7fefffffffffffff, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.mul_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} * {:#x}", op1, op2); - } - { - let a1 = DoubleDouble::from_bits(op1); - let mut a2 = DoubleDouble::from_bits(op2); - a2 = a2.mul_r(a1, round).value; - - assert_eq!(expected, a2.to_bits(), "{:#x} * {:#x}", op2, op1); - } - } -} - -#[test] -fn ppc_double_double_divide() { - // FIXME: Only a sanity check for now. Add more edge cases when the - // double-double algorithm is implemented. - let data = [ - // 1 / 3 = 1/3 - ( - 0x3ff0000000000000, - 0x4008000000000000, - 0x3c75555555555556_3fd5555555555555, - Round::NearestTiesToEven, - ), - ]; - - for (op1, op2, expected, round) in data { - let mut a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - a1 = a1.div_r(a2, round).value; - - assert_eq!(expected, a1.to_bits(), "{:#x} / {:#x}", op1, op2); - } -} - -#[test] -fn ppc_double_double_remainder() { - let data = [ - // ieee_rem(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53) - ( - 0x3cb8000000000000_4008000000000000, - 0x3ca4000000000000_3ff4000000000000, - 0x3c90000000000000_3fe0000000000000, - ), - // ieee_rem(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (-0.5 - 0.5 << 53) - ( - 0x3cb8000000000000_4008000000000000, - 0x3cac000000000000_3ffc000000000000, - 0xbc90000000000000_bfe0000000000000, - ), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - let result = a1.ieee_rem(a2).value; - - assert_eq!(expected, result.to_bits(), "ieee_rem({:#x}, {:#x})", op1, op2); - } -} - -#[test] -fn ppc_double_double_mod() { - let data = [ - // mod(3.0 + 3.0 << 53, 1.25 + 1.25 << 53) = (0.5 + 0.5 << 53) - ( - 0x3cb8000000000000_4008000000000000, - 0x3ca4000000000000_3ff4000000000000, - 0x3c90000000000000_3fe0000000000000, - ), - // mod(3.0 + 3.0 << 53, 1.75 + 1.75 << 53) = (1.25 + 1.25 << 53) - // 0xbc98000000000000 doesn't seem right, but it's what we currently have. - // FIXME: investigate - ( - 0x3cb8000000000000_4008000000000000, - 0x3cac000000000000_3ffc000000000000, - 0xbc98000000000000_3ff4000000000001, - ), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - let r = (a1 % a2).value; - - assert_eq!(expected, r.to_bits(), "fmod({:#x}, {:#x})", op1, op2); - } -} - -#[test] -fn ppc_double_double_fma() { - // Sanity check for now. - let mut a = "2".parse::().unwrap(); - a = a.mul_add("3".parse::().unwrap(), "4".parse::().unwrap()).value; - assert_eq!(Some(Ordering::Equal), "10".parse::().unwrap().partial_cmp(&a)); -} - -#[test] -fn ppc_double_double_round_to_integral() { - { - let a = "1.5".parse::().unwrap(); - let a = a.round_to_integral(Round::NearestTiesToEven).value; - assert_eq!(Some(Ordering::Equal), "2".parse::().unwrap().partial_cmp(&a)); - } - { - let a = "2.5".parse::().unwrap(); - let a = a.round_to_integral(Round::NearestTiesToEven).value; - assert_eq!(Some(Ordering::Equal), "2".parse::().unwrap().partial_cmp(&a)); - } -} - -#[test] -fn ppc_double_double_compare() { - let data = [ - // (1 + 0) = (1 + 0) - (0x3ff0000000000000, 0x3ff0000000000000, Some(Ordering::Equal)), - // (1 + 0) < (1.00...1 + 0) - (0x3ff0000000000000, 0x3ff0000000000001, Some(Ordering::Less)), - // (1.00...1 + 0) > (1 + 0) - (0x3ff0000000000001, 0x3ff0000000000000, Some(Ordering::Greater)), - // (1 + 0) < (1 + epsilon) - (0x3ff0000000000000, 0x0000000000000001_3ff0000000000001, Some(Ordering::Less)), - // NaN != NaN - (0x7ff8000000000000, 0x7ff8000000000000, None), - // (1 + 0) != NaN - (0x3ff0000000000000, 0x7ff8000000000000, None), - // Inf = Inf - (0x7ff0000000000000, 0x7ff0000000000000, Some(Ordering::Equal)), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - assert_eq!(expected, a1.partial_cmp(&a2), "compare({:#x}, {:#x})", op1, op2,); - } -} - -#[test] -fn ppc_double_double_bitwise_eq() { - let data = [ - // (1 + 0) = (1 + 0) - (0x3ff0000000000000, 0x3ff0000000000000, true), - // (1 + 0) != (1.00...1 + 0) - (0x3ff0000000000000, 0x3ff0000000000001, false), - // NaN = NaN - (0x7ff8000000000000, 0x7ff8000000000000, true), - // NaN != NaN with a different bit pattern - (0x7ff8000000000000, 0x3ff0000000000000_7ff8000000000000, false), - // Inf = Inf - (0x7ff0000000000000, 0x7ff0000000000000, true), - ]; - - for (op1, op2, expected) in data { - let a1 = DoubleDouble::from_bits(op1); - let a2 = DoubleDouble::from_bits(op2); - assert_eq!(expected, a1.bitwise_eq(a2), "{:#x} = {:#x}", op1, op2); - } -} - -#[test] -fn ppc_double_double_change_sign() { - let float = DoubleDouble::from_bits(0xbcb0000000000000_400f000000000000); - { - let actual = float.copy_sign("1".parse::().unwrap()); - assert_eq!(0xbcb0000000000000_400f000000000000, actual.to_bits()); - } - { - let actual = float.copy_sign("-1".parse::().unwrap()); - assert_eq!(0x3cb0000000000000_c00f000000000000, actual.to_bits()); - } -} - -#[test] -fn ppc_double_double_factories() { - assert_eq!(0, DoubleDouble::ZERO.to_bits()); - assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, DoubleDouble::largest().to_bits()); - assert_eq!(0x0000000000000001, DoubleDouble::SMALLEST.to_bits()); - assert_eq!(0x0360000000000000, DoubleDouble::smallest_normalized().to_bits()); - assert_eq!(0x0000000000000000_8000000000000000, (-DoubleDouble::ZERO).to_bits()); - assert_eq!(0xfc8ffffffffffffe_ffefffffffffffff, (-DoubleDouble::largest()).to_bits()); - assert_eq!(0x0000000000000000_8000000000000001, (-DoubleDouble::SMALLEST).to_bits()); - assert_eq!( - 0x0000000000000000_8360000000000000, - (-DoubleDouble::smallest_normalized()).to_bits() - ); - assert!(DoubleDouble::SMALLEST.is_smallest()); - assert!(DoubleDouble::largest().is_largest()); -} - -#[test] -fn ppc_double_double_is_denormal() { - assert!(DoubleDouble::SMALLEST.is_denormal()); - assert!(!DoubleDouble::largest().is_denormal()); - assert!(!DoubleDouble::smallest_normalized().is_denormal()); - { - // (4 + 3) is not normalized - let data = 0x4008000000000000_4010000000000000; - assert!(DoubleDouble::from_bits(data).is_denormal()); - } -} - -#[test] -fn ppc_double_double_exact_inverse() { - assert!( - "2.0" - .parse::() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::().unwrap()) - ); -} - -#[test] -fn ppc_double_double_scalbn() { - // 3.0 + 3.0 << 53 - let input = 0x3cb8000000000000_4008000000000000; - let result = DoubleDouble::from_bits(input).scalbn(1); - // 6.0 + 6.0 << 53 - assert_eq!(0x3cc8000000000000_4018000000000000, result.to_bits()); -} - -#[test] -fn ppc_double_double_frexp() { - // 3.0 + 3.0 << 53 - let input = 0x3cb8000000000000_4008000000000000; - let mut exp = 0; - // 0.75 + 0.75 << 53 - let result = DoubleDouble::from_bits(input).frexp(&mut exp); - assert_eq!(2, exp); - assert_eq!(0x3c98000000000000_3fe8000000000000, result.to_bits()); -} diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 6c781c6f0820..0bbb6c9c3381 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -362,7 +362,7 @@ pub struct CodegenContext { impl CodegenContext { pub fn create_diag_handler(&self) -> Handler { - Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone()), None) + Handler::with_emitter(Box::new(self.diag_emitter.clone())) } pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 74030a43c505..4e47fed86404 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tracing = "0.1" either = "1" -rustc_apfloat = { path = "../rustc_apfloat" } +rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index b21cb984de67..55d719d2703a 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -58,7 +58,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.push_stack_frame( cid.instance, body, - &ret.into(), + &ret.clone().into(), StackPopCleanup::Root { cleanup: false }, )?; @@ -356,7 +356,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // Since evaluation had no errors, validate the resulting constant. // This is a separate `try` block to provide more targeted error reporting. let validation: Result<_, InterpErrorInfo<'_>> = try { - let mut ref_tracking = RefTracking::new(mplace); + let mut ref_tracking = RefTracking::new(mplace.clone()); let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { let mode = match tcx.static_mutability(cid.instance.def_id()) { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 267795a6cb4a..6630eeca27ed 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -216,7 +216,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { let mut msg_place = self.deref_operand(&args[0])?; while msg_place.layout.ty.is_ref() { - msg_place = self.deref_operand(&msg_place.into())?; + msg_place = self.deref_operand(&msg_place)?; } let msg = Symbol::intern(self.read_str(&msg_place)?); diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index b519bcdf4a33..744dd114ca71 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -5,7 +5,7 @@ use crate::const_eval::CanAccessStatics; use crate::interpret::MPlaceTy; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, - MemoryKind, PlaceTy, Projectable, Scalar, + MemoryKind, Place, Projectable, Scalar, }; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::source_map::DUMMY_SP; @@ -21,7 +21,7 @@ fn branches<'tcx>( ) -> ValTreeCreationResult<'tcx> { let place = match variant { Some(variant) => ecx.project_downcast(place, variant).unwrap(), - None => *place, + None => place.clone(), }; let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); debug!(?place, ?variant); @@ -86,7 +86,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::zst()) } ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { - let Ok(val) = ecx.read_immediate(&place.into()) else { + let Ok(val) = ecx.read_immediate(place) else { return Err(ValTreeCreationError::Other); }; let val = val.to_scalar(); @@ -102,7 +102,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), ty::Ref(_, _, _) => { - let Ok(derefd_place)= ecx.deref_operand(&place.into()) else { + let Ok(derefd_place)= ecx.deref_operand(place) else { return Err(ValTreeCreationError::Other); }; debug!(?derefd_place); @@ -130,7 +130,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>( bug!("uninhabited types should have errored and never gotten converted to valtree") } - let Ok(variant) = ecx.read_discriminant(&place.into()) else { + let Ok(variant) = ecx.read_discriminant(place) else { return Err(ValTreeCreationError::Other); }; branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant), num_nodes) @@ -280,7 +280,7 @@ pub fn valtree_to_const_value<'tcx>( ), }, ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { - let mut place = match ty.kind() { + let place = match ty.kind() { ty::Ref(_, inner_ty, _) => { // Need to create a place for the pointee to fill for Refs create_pointee_place(&mut ecx, *inner_ty, valtree) @@ -289,8 +289,8 @@ pub fn valtree_to_const_value<'tcx>( }; debug!(?place); - valtree_into_mplace(&mut ecx, &mut place, valtree); - dump_place(&ecx, place.into()); + valtree_into_mplace(&mut ecx, &place, valtree); + dump_place(&ecx, &place); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); match ty.kind() { @@ -329,7 +329,7 @@ pub fn valtree_to_const_value<'tcx>( #[instrument(skip(ecx), level = "debug")] fn valtree_into_mplace<'tcx>( ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>, - place: &mut MPlaceTy<'tcx>, + place: &MPlaceTy<'tcx>, valtree: ty::ValTree<'tcx>, ) { // This will match on valtree and write the value(s) corresponding to the ValTree @@ -345,14 +345,14 @@ fn valtree_into_mplace<'tcx>( ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => { let scalar_int = valtree.unwrap_leaf(); debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place); - ecx.write_immediate(Immediate::Scalar(scalar_int.into()), &place.into()).unwrap(); + ecx.write_immediate(Immediate::Scalar(scalar_int.into()), place).unwrap(); } ty::Ref(_, inner_ty, _) => { - let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree); + let pointee_place = create_pointee_place(ecx, *inner_ty, valtree); debug!(?pointee_place); - valtree_into_mplace(ecx, &mut pointee_place, valtree); - dump_place(ecx, pointee_place.into()); + valtree_into_mplace(ecx, &pointee_place, valtree); + dump_place(ecx, &pointee_place); intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap(); let imm = match inner_ty.kind() { @@ -369,7 +369,7 @@ fn valtree_into_mplace<'tcx>( }; debug!(?imm); - ecx.write_immediate(imm, &place.into()).unwrap(); + ecx.write_immediate(imm, place).unwrap(); } ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => { let branches = valtree.unwrap_branch(); @@ -389,7 +389,7 @@ fn valtree_into_mplace<'tcx>( Some(variant_idx), ) } - _ => (*place, branches, None), + _ => (place.clone(), branches, None), }; debug!(?place_adjusted, ?branches); @@ -398,7 +398,7 @@ fn valtree_into_mplace<'tcx>( for (i, inner_valtree) in branches.iter().enumerate() { debug!(?i, ?inner_valtree); - let mut place_inner = match ty.kind() { + let place_inner = match ty.kind() { ty::Str | ty::Slice(_) => ecx.project_index(place, i as u64).unwrap(), _ if !ty.is_sized(*ecx.tcx, ty::ParamEnv::empty()) && i == branches.len() - 1 => @@ -443,25 +443,25 @@ fn valtree_into_mplace<'tcx>( }; debug!(?place_inner); - valtree_into_mplace(ecx, &mut place_inner, *inner_valtree); - dump_place(&ecx, place_inner.into()); + valtree_into_mplace(ecx, &place_inner, *inner_valtree); + dump_place(&ecx, &place_inner); } debug!("dump of place_adjusted:"); - dump_place(ecx, place_adjusted.into()); + dump_place(ecx, &place_adjusted); if let Some(variant_idx) = variant_idx { // don't forget filling the place with the discriminant of the enum - ecx.write_discriminant(variant_idx, &place.into()).unwrap(); + ecx.write_discriminant(variant_idx, place).unwrap(); } debug!("dump of place after writing discriminant:"); - dump_place(ecx, place.into()); + dump_place(ecx, place); } _ => bug!("shouldn't have created a ValTree for {:?}", ty), } } -fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: PlaceTy<'tcx>) { - trace!("{:?}", ecx.dump_place(*place)); +fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>) { + trace!("{:?}", ecx.dump_place(Place::Ptr(**place))); } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 977e49b63431..98e853dc4d9e 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -56,7 +56,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } CastKind::FnPtrToPtr | CastKind::PtrToPtr => { - let src = self.read_immediate(&src)?; + let src = self.read_immediate(src)?; let res = self.ptr_to_ptr(&src, cast_ty)?; self.write_immediate(res, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index aff86d5f4866..6c35fb01a93b 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -5,7 +5,7 @@ use rustc_middle::{mir, ty}; use rustc_target::abi::{self, TagEncoding}; use rustc_target::abi::{VariantIdx, Variants}; -use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar}; +use super::{ImmTy, InterpCx, InterpResult, Machine, Readable, Scalar, Writeable}; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Writes the discriminant of the given variant. @@ -13,7 +13,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn write_discriminant( &mut self, variant_index: VariantIdx, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { // Layout computation excludes uninhabited variants from consideration // therefore there's no way to represent those variants in the given layout. @@ -21,11 +21,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // discriminant, so we cannot do anything here. // When evaluating we will always error before even getting here, but ConstProp 'executes' // dead code, so we cannot ICE here. - if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() { + if dest.layout().for_variant(self, variant_index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantWritten(variant_index)) } - match dest.layout.variants { + match dest.layout().variants { abi::Variants::Single { index } => { assert_eq!(index, variant_index); } @@ -38,8 +38,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // No need to validate that the discriminant here because the // `TyAndLayout::for_variant()` call earlier already checks the variant is valid. - let discr_val = - dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val; + let discr_val = dest + .layout() + .ty + .discriminant_for_variant(*self.tcx, variant_index) + .unwrap() + .val; // raw discriminants for enums are isize or bigger during // their computation, but the in-memory tag is the smallest possible @@ -92,11 +96,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[instrument(skip(self), level = "trace")] pub fn read_discriminant( &self, - op: &OpTy<'tcx, M::Provenance>, + op: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, VariantIdx> { - trace!("read_discriminant_value {:#?}", op.layout); + let ty = op.layout().ty; + trace!("read_discriminant_value {:#?}", op.layout()); // Get type and layout of the discriminant. - let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?; + let discr_layout = self.layout_of(ty.discriminant_ty(*self.tcx))?; trace!("discriminant type: {:?}", discr_layout.ty); // We use "discriminant" to refer to the value associated with a particular enum variant. @@ -104,20 +109,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // declared list of variants -- they can differ with explicitly assigned discriminants. // We use "tag" to refer to how the discriminant is encoded in memory, which can be either // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`). - let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants { + let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants { Variants::Single { index } => { // Do some extra checks on enums. - if op.layout.ty.is_enum() { + if ty.is_enum() { // Hilariously, `Single` is used even for 0-variant enums. // (See https://github.com/rust-lang/rust/issues/89765). - if matches!(op.layout.ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) - { + if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { throw_ub!(UninhabitedEnumVariantRead(index)) } // For consisteny with `write_discriminant`, and to make sure that // `project_downcast` cannot fail due to strange layouts, we declare immediate UB // for uninhabited variants. - if op.layout.for_variant(self, index).abi.is_uninhabited() { + if op.layout().for_variant(self, index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantRead(index)) } } @@ -163,7 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap(); let discr_bits = discr_val.assert_bits(discr_layout.size); // Convert discriminant to variant index, and catch invalid discriminants. - let index = match *op.layout.ty.kind() { + let index = match *ty.kind() { ty::Adt(adt, _) => { adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) } @@ -217,12 +221,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .checked_add(variant_index_relative) .expect("overflow computing absolute variant idx"), ); - let variants = op - .layout - .ty - .ty_adt_def() - .expect("tagged layout for non adt") - .variants(); + let variants = + ty.ty_adt_def().expect("tagged layout for non adt").variants(); assert!(variant_index < variants.next_index()); variant_index } else { @@ -237,7 +237,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } }; // For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants. - if op.layout.for_variant(self, index).abi.is_uninhabited() { + if op.layout().for_variant(self, index).abi.is_uninhabited() { throw_ub!(UninhabitedEnumVariantRead(index)) } Ok(index) diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 3a7fe8bd4781..910c3ca5d0a9 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -170,7 +170,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory let tcx = self.ecx.tcx; let ty = mplace.layout.ty; if let ty::Ref(_, referenced_ty, ref_mutability) = *ty.kind() { - let value = self.ecx.read_immediate(&mplace.into())?; + let value = self.ecx.read_immediate(mplace)?; let mplace = self.ecx.ref_to_mplace(&value)?; assert_eq!(mplace.layout.ty, referenced_ty); // Handle trait object vtables. @@ -358,7 +358,7 @@ pub fn intern_const_alloc_recursive< Some(ret.layout.ty), ); - ref_tracking.track((*ret, base_intern_mode), || ()); + ref_tracking.track((ret.clone(), base_intern_mode), || ()); while let Some(((mplace, mode), _)) = ref_tracking.todo.pop() { let res = InternVisitor { @@ -464,7 +464,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>> ) -> InterpResult<'tcx, ()>, ) -> InterpResult<'tcx, ConstAllocation<'tcx>> { let dest = self.allocate(layout, MemoryKind::Stack)?; - f(self, &dest.into())?; + f(self, &dest.clone().into())?; let mut alloc = self.memory.alloc_map.remove(&dest.ptr.provenance.unwrap()).unwrap().1; alloc.mutability = Mutability::Not; Ok(self.tcx.mk_const_alloc(alloc)) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 3f6971682805..4020d96b80ef 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -226,7 +226,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::discriminant_value => { let place = self.deref_operand(&args[0])?; - let variant = self.read_discriminant(&place.into())?; + let variant = self.read_discriminant(&place)?; let discr = self.discriminant_for_variant(place.layout, variant)?; self.write_scalar(discr, dest)?; } @@ -432,7 +432,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { self.project_index(&input, i)?.into() }; - self.copy_op(&value, &place.into(), /*allow_transmute*/ false)?; + self.copy_op(&value, &place, /*allow_transmute*/ false)?; } } sym::simd_extract => { @@ -445,7 +445,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { input_len ); self.copy_op( - &self.project_index(&input, index)?.into(), + &self.project_index(&input, index)?, dest, /*allow_transmute*/ false, )?; @@ -610,7 +610,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { count: &OpTy<'tcx, >::Provenance>, nonoverlapping: bool, ) -> InterpResult<'tcx> { - let count = self.read_target_usize(&count)?; + let count = self.read_target_usize(count)?; let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?; let (size, align) = (layout.size, layout.align.abi); // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max), @@ -622,8 +622,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) })?; - let src = self.read_pointer(&src)?; - let dst = self.read_pointer(&dst)?; + let src = self.read_pointer(src)?; + let dst = self.read_pointer(dst)?; self.mem_copy(src, align, dst, align, size, nonoverlapping) } @@ -636,9 +636,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx> { let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap().ty)?; - let dst = self.read_pointer(&dst)?; - let byte = self.read_scalar(&byte)?.to_u8()?; - let count = self.read_target_usize(&count)?; + let dst = self.read_pointer(dst)?; + let byte = self.read_scalar(byte)?.to_u8()?; + let count = self.read_target_usize(count)?; // `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max), // but no actual allocation can be big enough for the difference to be noticeable. diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs index 44a12751743d..948bec7464ad 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs @@ -101,11 +101,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let location = self.allocate(loc_layout, MemoryKind::CallerLocation).unwrap(); // Initialize fields. - self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap().into()) + self.write_immediate(file.to_ref(self), &self.project_field(&location, 0).unwrap()) .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(line, &self.project_field(&location, 1).unwrap().into()) + self.write_scalar(line, &self.project_field(&location, 1).unwrap()) .expect("writing to memory we just allocated cannot fail"); - self.write_scalar(col, &self.project_field(&location, 2).unwrap().into()) + self.write_scalar(col, &self.project_field(&location, 2).unwrap()) .expect("writing to memory we just allocated cannot fail"); location diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 7974920bc14d..b0b553c45d43 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -24,8 +24,8 @@ pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, pub use self::intern::{intern_const_alloc_recursive, InternKind}; pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; -pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; -pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; +pub use self::operand::{ImmTy, Immediate, OpTy, Operand, Readable}; +pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy, Writeable}; pub use self::projection::Projectable; pub use self::terminator::FnArg; pub use self::validity::{CtfeValidationMode, RefTracking}; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index be39e63ab4f5..d1427b09632d 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -180,20 +180,6 @@ impl<'tcx, Prov: Provenance> From> for OpTy<'tcx, Prov> { } } -impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { - #[inline(always)] - fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { - OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } - } -} - -impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> { - #[inline(always)] - fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { - OpTy { op: Operand::Indirect(**mplace), layout: mplace.layout, align: Some(mplace.align) } - } -} - impl<'tcx, Prov: Provenance> From> for OpTy<'tcx, Prov> { #[inline(always)] fn from(val: ImmTy<'tcx, Prov>) -> Self { @@ -201,20 +187,6 @@ impl<'tcx, Prov: Provenance> From> for OpTy<'tcx, Prov> { } } -impl<'tcx, Prov: Provenance> From<&'_ ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { - #[inline(always)] - fn from(val: &ImmTy<'tcx, Prov>) -> Self { - OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } - } -} - -impl<'tcx, Prov: Provenance> From<&'_ mut ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { - #[inline(always)] - fn from(val: &mut ImmTy<'tcx, Prov>) -> Self { - OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None } - } -} - impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar(val: Scalar, layout: TyAndLayout<'tcx>) -> Self { @@ -312,13 +284,13 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { } } -impl<'mir, 'tcx: 'mir, Prov: Provenance> Projectable<'mir, 'tcx, Prov> for ImmTy<'tcx, Prov> { +impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { self.layout } - fn meta>( + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, MemPlaceMeta> { @@ -337,11 +309,11 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance> Projectable<'mir, 'tcx, Prov> for ImmTy Ok(self.offset_(offset, layout, cx)) } - fn to_op>( + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) + Ok(self.clone().into()) } } @@ -362,15 +334,13 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { } } -impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> - for OpTy<'tcx, Prov> -{ +impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { self.layout } - fn meta>( + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, MemPlaceMeta> { @@ -394,7 +364,7 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> } } - fn to_op>( + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { @@ -402,6 +372,31 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> } } +pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { + fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>>; +} + +impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for OpTy<'tcx, Prov> { + #[inline(always)] + fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>> { + self.as_mplace_or_imm() + } +} + +impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { + #[inline(always)] + fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>> { + Left(self.clone()) + } +} + +impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> { + #[inline(always)] + fn as_mplace_or_imm(&self) -> Either, ImmTy<'tcx, Prov>> { + Right(self.clone()) + } +} + impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`. /// Returns `None` if the layout does not permit loading this as a value. @@ -474,14 +469,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// ConstProp needs it, though. pub fn read_immediate_raw( &self, - src: &OpTy<'tcx, M::Provenance>, + src: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Either, ImmTy<'tcx, M::Provenance>>> { Ok(match src.as_mplace_or_imm() { Left(ref mplace) => { if let Some(val) = self.read_immediate_from_mplace_raw(mplace)? { Right(val) } else { - Left(*mplace) + Left(mplace.clone()) } } Right(val) => Right(val), @@ -494,14 +489,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn read_immediate( &self, - op: &OpTy<'tcx, M::Provenance>, + op: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { if !matches!( - op.layout.abi, + op.layout().abi, Abi::Scalar(abi::Scalar::Initialized { .. }) | Abi::ScalarPair(abi::Scalar::Initialized { .. }, abi::Scalar::Initialized { .. }) ) { - span_bug!(self.cur_span(), "primitive read not possible for type: {:?}", op.layout.ty); + span_bug!( + self.cur_span(), + "primitive read not possible for type: {:?}", + op.layout().ty + ); } let imm = self.read_immediate_raw(op)?.right().unwrap(); if matches!(*imm, Immediate::Uninit) { @@ -513,7 +512,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read a scalar from a place pub fn read_scalar( &self, - op: &OpTy<'tcx, M::Provenance>, + op: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Scalar> { Ok(self.read_immediate(op)?.to_scalar()) } @@ -524,16 +523,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Read a pointer from a place. pub fn read_pointer( &self, - op: &OpTy<'tcx, M::Provenance>, + op: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, Pointer>> { self.read_scalar(op)?.to_pointer(self) } /// Read a pointer-sized unsigned integer from a place. - pub fn read_target_usize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, u64> { + pub fn read_target_usize( + &self, + op: &impl Readable<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, u64> { self.read_scalar(op)?.to_target_usize(self) } /// Read a pointer-sized signed integer from a place. - pub fn read_target_isize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, i64> { + pub fn read_target_isize( + &self, + op: &impl Readable<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, i64> { self.read_scalar(op)?.to_target_isize(self) } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index db1239c7136a..96a960118ce1 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -18,7 +18,7 @@ use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_V use super::{ alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ConstAlloc, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, - Pointer, Projectable, Provenance, Scalar, + Pointer, Projectable, Provenance, Readable, Scalar, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -81,7 +81,7 @@ pub struct MemPlace { } /// A MemPlace with its layout. Constructing it is only possible in this module. -#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] +#[derive(Clone, Hash, Eq, PartialEq, Debug)] pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> { mplace: MemPlace, pub layout: TyAndLayout<'tcx>, @@ -92,6 +92,14 @@ pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> { pub align: Align, } +impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> { + type Target = MemPlace; + #[inline(always)] + fn deref(&self) -> &MemPlace { + &self.mplace + } +} + #[derive(Copy, Clone, Debug)] pub enum Place { /// A place referring to a value allocated in the `Memory` system. @@ -125,14 +133,6 @@ impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> { } } -impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> { - type Target = MemPlace; - #[inline(always)] - fn deref(&self) -> &MemPlace { - &self.mplace - } -} - impl<'tcx, Prov: Provenance> From> for PlaceTy<'tcx, Prov> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { @@ -140,20 +140,6 @@ impl<'tcx, Prov: Provenance> From> for PlaceTy<'tcx, Prov> } } -impl<'tcx, Prov: Provenance> From<&'_ MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { - #[inline(always)] - fn from(mplace: &MPlaceTy<'tcx, Prov>) -> Self { - PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } - } -} - -impl<'tcx, Prov: Provenance> From<&'_ mut MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { - #[inline(always)] - fn from(mplace: &mut MPlaceTy<'tcx, Prov>) -> Self { - PlaceTy { place: Place::Ptr(**mplace), layout: mplace.layout, align: mplace.align } - } -} - impl MemPlace { #[inline(always)] pub fn from_ptr(ptr: Pointer>) -> Self { @@ -229,15 +215,13 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { } } -impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> - for MPlaceTy<'tcx, Prov> -{ +impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { self.layout } - fn meta>( + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, MemPlaceMeta> { @@ -258,11 +242,59 @@ impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> }) } - fn to_op>( + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) + Ok(self.clone().into()) + } +} + +impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta> { + ecx.place_meta(self) + } + + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + Ok(match self.as_mplace_or_local() { + Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(), + Right((frame, local, old_offset)) => { + assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway... + let new_offset = cx + .data_layout() + .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?; + PlaceTy { + place: Place::Local { + frame, + local, + offset: Some(Size::from_bytes(new_offset)), + }, + align: self.align.restrict_for_offset(offset), + layout, + } + } + }) + } + + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + ecx.place_to_op(self) } } @@ -314,53 +346,51 @@ impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> { } } -impl<'mir, 'tcx: 'mir, Prov: Provenance + 'static> Projectable<'mir, 'tcx, Prov> - for PlaceTy<'tcx, Prov> -{ +pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { + fn as_mplace_or_local( + &self, + ) -> Either, (usize, mir::Local, Option, Align, TyAndLayout<'tcx>)>; + + fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>>; +} + +impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> { #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout + fn as_mplace_or_local( + &self, + ) -> Either, (usize, mir::Local, Option, Align, TyAndLayout<'tcx>)> + { + self.as_mplace_or_local() + .map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout)) } - fn meta>( + #[inline(always)] + fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, MemPlaceMeta> { - ecx.place_meta(self) + ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { + ecx.force_allocation(self) + } +} + +impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { + #[inline(always)] + fn as_mplace_or_local( + &self, + ) -> Either, (usize, mir::Local, Option, Align, TyAndLayout<'tcx>)> + { + Left(self.clone()) } - fn offset_with_meta( + #[inline(always)] + fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, - offset: Size, - meta: MemPlaceMeta, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - Ok(match self.as_mplace_or_local() { - Left(mplace) => mplace.offset_with_meta(offset, meta, layout, cx)?.into(), - Right((frame, local, old_offset)) => { - assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway... - let new_offset = cx - .data_layout() - .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?; - PlaceTy { - place: Place::Local { - frame, - local, - offset: Some(Size::from_bytes(new_offset)), - }, - align: self.align.restrict_for_offset(offset), - layout, - } - } - }) - } - - fn to_op>( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - ecx.place_to_op(self) + _ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { + Ok(self.clone()) } } @@ -412,7 +442,7 @@ where #[instrument(skip(self), level = "debug")] pub fn deref_operand( &self, - src: &OpTy<'tcx, M::Provenance>, + src: &impl Readable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); @@ -422,7 +452,7 @@ where } let mplace = self.ref_to_mplace(&val)?; - self.check_mplace(mplace)?; + self.check_mplace(&mplace)?; Ok(mplace) } @@ -453,7 +483,7 @@ where } /// Check if this mplace is dereferenceable and sufficiently aligned. - pub fn check_mplace(&self, mplace: MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + pub fn check_mplace(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { let (size, _align) = self .size_and_align_of_mplace(&mplace)? .unwrap_or((mplace.layout.size, mplace.layout.align.abi)); @@ -537,13 +567,13 @@ where pub fn write_immediate( &mut self, src: Immediate, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate_no_validate(src, dest)?; - if M::enforce_validity(self, dest.layout) { + if M::enforce_validity(self, dest.layout()) { // Data got changed, better make sure it matches the type! - self.validate_operand(&self.place_to_op(dest)?)?; + self.validate_operand(&dest.to_op(self)?)?; } Ok(()) @@ -554,7 +584,7 @@ where pub fn write_scalar( &mut self, val: impl Into>, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate(Immediate::Scalar(val.into()), dest) } @@ -564,7 +594,7 @@ where pub fn write_pointer( &mut self, ptr: impl Into>>, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest) } @@ -575,20 +605,19 @@ where fn write_immediate_no_validate( &mut self, src: Immediate, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - assert!(dest.layout.is_sized(), "Cannot write unsized immediate data"); - trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); + assert!(dest.layout().is_sized(), "Cannot write unsized immediate data"); // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, // but not factored as a separate function. - let mplace = match dest.place { - Place::Local { frame, local, offset } => { + let mplace = match dest.as_mplace_or_local() { + Right((frame, local, offset, align, layout)) => { if offset.is_some() { // This has been projected to a part of this local. We could have complicated // logic to still keep this local as an `Operand`... but it's much easier to // just fall back to the indirect path. - *self.force_allocation(dest)? + dest.force_mplace(self)? } else { match M::access_local_mut(self, frame, local)? { Operand::Immediate(local_val) => { @@ -623,16 +652,16 @@ where } Operand::Indirect(mplace) => { // The local is in memory, go on below. - *mplace + MPlaceTy { mplace: *mplace, align, layout } } } } } - Place::Ptr(mplace) => mplace, // already referring to memory + Left(mplace) => mplace, // already referring to memory }; // This is already in memory, write there. - self.write_immediate_to_mplace_no_validate(src, dest.layout, dest.align, mplace) + self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.align, mplace.mplace) } /// Write an immediate to memory. @@ -696,16 +725,19 @@ where } } - pub fn write_uninit(&mut self, dest: &PlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> { + pub fn write_uninit( + &mut self, + dest: &impl Writeable<'tcx, M::Provenance>, + ) -> InterpResult<'tcx> { let mplace = match dest.as_mplace_or_local() { Left(mplace) => mplace, - Right((frame, local, offset)) => { + Right((frame, local, offset, align, layout)) => { if offset.is_some() { // This has been projected to a part of this local. We could have complicated // logic to still keep this local as an `Operand`... but it's much easier to // just fall back to the indirect path. // FIXME: share the logic with `write_immediate_no_validate`. - self.force_allocation(dest)? + dest.force_mplace(self)? } else { match M::access_local_mut(self, frame, local)? { Operand::Immediate(local) => { @@ -714,7 +746,7 @@ where } Operand::Indirect(mplace) => { // The local is in memory, go on below. - MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align } + MPlaceTy { mplace: *mplace, layout, align } } } } @@ -734,15 +766,15 @@ where #[instrument(skip(self), level = "debug")] pub fn copy_op( &mut self, - src: &OpTy<'tcx, M::Provenance>, - dest: &PlaceTy<'tcx, M::Provenance>, + src: &impl Readable<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { self.copy_op_no_validate(src, dest, allow_transmute)?; - if M::enforce_validity(self, dest.layout) { + if M::enforce_validity(self, dest.layout()) { // Data got changed, better make sure it matches the type! - self.validate_operand(&self.place_to_op(dest)?)?; + self.validate_operand(&dest.to_op(self)?)?; } Ok(()) @@ -755,20 +787,20 @@ where #[instrument(skip(self), level = "debug")] fn copy_op_no_validate( &mut self, - src: &OpTy<'tcx, M::Provenance>, - dest: &PlaceTy<'tcx, M::Provenance>, + src: &impl Readable<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, allow_transmute: bool, ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can // actually "transmute" `&mut T` to `&T` in an assignment without a cast. let layout_compat = - mir_assign_valid_types(*self.tcx, self.param_env, src.layout, dest.layout); + mir_assign_valid_types(*self.tcx, self.param_env, src.layout(), dest.layout()); if !allow_transmute && !layout_compat { span_bug!( self.cur_span(), "type mismatch when copying!\nsrc: {:?},\ndest: {:?}", - src.layout.ty, - dest.layout.ty, + src.layout().ty, + dest.layout().ty, ); } @@ -781,13 +813,13 @@ where // actually sized, due to a trivially false where-clause // predicate like `where Self: Sized` with `Self = dyn Trait`. // See #102553 for an example of such a predicate. - if src.layout.is_unsized() { - throw_inval!(SizeOfUnsizedType(src.layout.ty)); + if src.layout().is_unsized() { + throw_inval!(SizeOfUnsizedType(src.layout().ty)); } - if dest.layout.is_unsized() { - throw_inval!(SizeOfUnsizedType(dest.layout.ty)); + if dest.layout().is_unsized() { + throw_inval!(SizeOfUnsizedType(dest.layout().ty)); } - assert_eq!(src.layout.size, dest.layout.size); + assert_eq!(src.layout().size, dest.layout().size); // Yay, we got a value that we can write directly. return if layout_compat { self.write_immediate_no_validate(*src_val, dest) @@ -796,10 +828,10 @@ where // loaded using the offsets defined by `src.layout`. When we put this back into // the destination, we have to use the same offsets! So (a) we make sure we // write back to memory, and (b) we use `dest` *with the source layout*. - let dest_mem = self.force_allocation(dest)?; + let dest_mem = dest.force_mplace(self)?; self.write_immediate_to_mplace_no_validate( *src_val, - src.layout, + src.layout(), dest_mem.align, *dest_mem, ) @@ -808,9 +840,9 @@ where Left(mplace) => mplace, }; // Slow path, this does not fit into an immediate. Just memcpy. - trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout.ty); + trace!("copy_op: {:?} <- {:?}: {}", *dest, src, dest.layout().ty); - let dest = self.force_allocation(&dest)?; + let dest = dest.force_mplace(self)?; let Some((dest_size, _)) = self.size_and_align_of_mplace(&dest)? else { span_bug!(self.cur_span(), "copy_op needs (dynamically) sized values") }; @@ -928,7 +960,7 @@ where operands: &IndexSlice>, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - self.write_uninit(&dest)?; + self.write_uninit(dest)?; let (variant_index, variant_dest, active_field_index) = match *kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { let variant_dest = self.project_downcast(dest, variant_index)?; @@ -945,7 +977,7 @@ where let op = self.eval_operand(operand, Some(field_dest.layout))?; self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?; } - self.write_discriminant(variant_index, &dest) + self.write_discriminant(variant_index, dest) } pub fn raw_const_to_mplace( @@ -983,7 +1015,7 @@ where /// Turn a `dyn* Trait` type into an value with the actual dynamic type. /// Also returns the vtable. - pub(super) fn unpack_dyn_star>( + pub(super) fn unpack_dyn_star>( &self, val: &P, ) -> InterpResult<'tcx, (P, Pointer>)> { diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index ddcbc8350aae..bce43aedb69c 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -16,21 +16,20 @@ use rustc_target::abi::HasDataLayout; use rustc_target::abi::Size; use rustc_target::abi::{self, VariantIdx}; -use super::MPlaceTy; -use super::{InterpCx, InterpResult, Machine, MemPlaceMeta, OpTy, Provenance, Scalar}; +use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar}; /// A thing that we can project into, and that has a layout. -pub trait Projectable<'mir, 'tcx: 'mir, Prov: Provenance>: Sized { +pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug { /// Get the layout. fn layout(&self) -> TyAndLayout<'tcx>; /// Get the metadata of a wide value. - fn meta>( + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, MemPlaceMeta>; - fn len>( + fn len<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, u64> { @@ -67,7 +66,7 @@ pub trait Projectable<'mir, 'tcx: 'mir, Prov: Provenance>: Sized { /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for /// reading from this thing. - fn to_op>( + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; @@ -85,7 +84,7 @@ where /// /// This also works for arrays, but then the `usize` index type is restricting. /// For indexing into arrays, use `mplace_index`. - pub fn project_field>( + pub fn project_field>( &self, base: &P, field: usize, @@ -128,7 +127,7 @@ where } /// Downcasting to an enum variant. - pub fn project_downcast>( + pub fn project_downcast>( &self, base: &P, variant: VariantIdx, @@ -149,7 +148,7 @@ where } /// Compute the offset and field layout for accessing the given index. - pub fn project_index>( + pub fn project_index>( &self, base: &P, index: u64, @@ -178,7 +177,7 @@ where base.offset(offset, field_layout, self) } - fn project_constant_index>( + fn project_constant_index>( &self, base: &P, offset: u64, @@ -204,7 +203,7 @@ where /// Iterates over all fields of an array. Much more efficient than doing the /// same by repeatedly calling `operand_index`. - pub fn project_array_fields<'a, P: Projectable<'mir, 'tcx, M::Provenance>>( + pub fn project_array_fields<'a, P: Projectable<'tcx, M::Provenance>>( &self, base: &'a P, ) -> InterpResult<'tcx, impl Iterator> + 'a> @@ -224,7 +223,7 @@ where } /// Subslicing - fn project_subslice>( + fn project_subslice>( &self, base: &P, from: u64, @@ -284,9 +283,7 @@ where #[instrument(skip(self), level = "trace")] pub fn project

(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P> where - P: Projectable<'mir, 'tcx, M::Provenance> - + From> - + std::fmt::Debug, + P: Projectable<'tcx, M::Provenance> + From> + std::fmt::Debug, { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 9182d23128fe..1f2d04f40089 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -198,7 +198,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { // Write the src to the first element. let first = self.project_index(&dest, 0)?; - self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?; + self.copy_op(&src, &first, /*allow_transmute*/ false)?; // This is performance-sensitive code for big static/const arrays! So we // avoid writing each operand individually and instead just make many copies diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index f934cca2517b..d0191ea978a4 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -634,7 +634,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Ensure the return place is aligned and dereferenceable, and protect it for // in-place return value passing. if let Either::Left(mplace) = destination.as_mplace_or_local() { - self.check_mplace(mplace)?; + self.check_mplace(&mplace)?; } else { // Nothing to do for locals, they are always properly allocated and aligned. } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index a82c98e72057..4fd5fd13c3cf 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -136,19 +136,19 @@ pub struct RefTracking { pub todo: Vec<(T, PATH)>, } -impl RefTracking { +impl RefTracking { pub fn empty() -> Self { RefTracking { seen: FxHashSet::default(), todo: vec![] } } pub fn new(op: T) -> Self { let mut ref_tracking_for_consts = - RefTracking { seen: FxHashSet::default(), todo: vec![(op, PATH::default())] }; + RefTracking { seen: FxHashSet::default(), todo: vec![(op.clone(), PATH::default())] }; ref_tracking_for_consts.seen.insert(op); ref_tracking_for_consts } pub fn track(&mut self, op: T, path: impl FnOnce() -> PATH) { - if self.seen.insert(op) { + if self.seen.insert(op.clone()) { trace!("Recursing below ptr {:#?}", op); let path = path(); // Remember to come back to this later. diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 4ec19d9e655b..531e2bd3ee06 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -13,9 +13,7 @@ use super::{InterpCx, MPlaceTy, Machine, Projectable}; /// How to traverse a value and what to do when we are at the leaves. pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - type V: Projectable<'mir, 'tcx, M::Provenance> - + From> - + std::fmt::Debug; + type V: Projectable<'tcx, M::Provenance> + From>; /// The visitor must have an `InterpCx` in it. fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8722979cb5ef..8f8b9eaa274a 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -1415,7 +1415,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: false, TerminalUrl::No, )); - let handler = rustc_errors::Handler::with_emitter(true, None, emitter, None); + let handler = rustc_errors::Handler::with_emitter(emitter); // a .span_bug or .bug call has already printed what // it wants to print. diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 08ff2cfba5c2..5e23ae655fe8 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -536,7 +536,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { } }; - if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() { + if handler.inner.lock().flags.dont_buffer_diagnostics + || handler.inner.lock().flags.treat_err_as_bug.is_some() + { self.emit(); return None; } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index db0dd4ffe8e1..1f9a2981e02b 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -64,7 +64,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { ); let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1)); - let handler = Handler::with_emitter(true, None, Box::new(je), None); + let handler = Handler::with_emitter(Box::new(je)); handler.span_err(span, "foo"); let bytes = output.lock().unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index eae7a46e07a3..d18e01d178e6 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -391,7 +391,6 @@ use std::backtrace::{Backtrace, BacktraceStatus}; /// Certain errors (fatal, bug, unimpl) may cause immediate exit, /// others log errors for later reporting. pub struct Handler { - flags: HandlerFlags, inner: Lock, } @@ -549,69 +548,47 @@ impl Drop for HandlerInner { impl Handler { pub fn with_tty_emitter( - color_config: ColorConfig, - can_emit_warnings: bool, - treat_err_as_bug: Option, sm: Option>, - fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, - ice_file: Option, - ) -> Self { - Self::with_tty_emitter_and_flags( - color_config, - sm, - fluent_bundle, - fallback_bundle, - HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, - ice_file, - ) - } - - pub fn with_tty_emitter_and_flags( - color_config: ColorConfig, - sm: Option>, - fluent_bundle: Option>, - fallback_bundle: LazyFallbackBundle, - flags: HandlerFlags, - ice_file: Option, ) -> Self { let emitter = Box::new(EmitterWriter::stderr( - color_config, + ColorConfig::Auto, sm, - fluent_bundle, + None, fallback_bundle, false, false, None, - flags.macro_backtrace, - flags.track_diagnostics, + false, + false, TerminalUrl::No, )); - Self::with_emitter_and_flags(emitter, flags, ice_file) + Self::with_emitter(emitter) + } + pub fn disable_warnings(mut self) -> Self { + self.inner.get_mut().flags.can_emit_warnings = false; + self } - pub fn with_emitter( - can_emit_warnings: bool, - treat_err_as_bug: Option, - emitter: Box, - ice_file: Option, - ) -> Self { - Handler::with_emitter_and_flags( - emitter, - HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, - ice_file, - ) + pub fn treat_err_as_bug(mut self, treat_err_as_bug: NonZeroUsize) -> Self { + self.inner.get_mut().flags.treat_err_as_bug = Some(treat_err_as_bug); + self } - pub fn with_emitter_and_flags( - emitter: Box, - flags: HandlerFlags, - ice_file: Option, - ) -> Self { + pub fn with_flags(mut self, flags: HandlerFlags) -> Self { + self.inner.get_mut().flags = flags; + self + } + + pub fn with_ice_file(mut self, ice_file: PathBuf) -> Self { + self.inner.get_mut().ice_file = Some(ice_file); + self + } + + pub fn with_emitter(emitter: Box) -> Self { Self { - flags, inner: Lock::new(HandlerInner { - flags, + flags: HandlerFlags { can_emit_warnings: true, ..Default::default() }, lint_err_count: 0, err_count: 0, warn_count: 0, @@ -629,7 +606,7 @@ impl Handler { check_unstable_expect_diagnostics: false, unstable_expect_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), - ice_file, + ice_file: None, }), } } @@ -657,7 +634,7 @@ impl Handler { // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { - self.flags.can_emit_warnings + self.inner.lock().flags.can_emit_warnings } /// Resets the diagnostic error count as well as the cached emitted diagnostics. diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 8b37728b60fe..e133501c5d0a 100644 --- a/compiler/rustc_expand/src/parse/tests.rs +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -1,4 +1,6 @@ -use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse}; +use crate::tests::{ + matches_codepattern, string_to_stream, with_error_checking_parse, with_expected_parse_error, +}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token}; @@ -51,11 +53,15 @@ fn string_to_item(source_str: String) -> Option> { with_error_checking_parse(source_str, &sess(), |p| p.parse_item(ForceCollect::No)) } -#[should_panic] #[test] fn bad_path_expr_1() { + // This should trigger error: expected identifier, found keyword `return` create_default_session_globals_then(|| { - string_to_expr("::abc::def::return".to_string()); + with_expected_parse_error( + "::abc::def::return", + "expected identifier, found keyword `return`", + |p| p.parse_expr(), + ); }) } diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 6490e52955db..30fa5fea4076 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -22,6 +22,33 @@ fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> { new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str) } +fn create_test_handler() -> (Handler, Lrc, Arc>>) { + let output = Arc::new(Mutex::new(Vec::new())); + let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let fallback_bundle = rustc_errors::fallback_fluent_bundle( + vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], + false, + ); + let emitter = EmitterWriter::new( + Box::new(Shared { data: output.clone() }), + Some(source_map.clone()), + None, + fallback_bundle, + false, + false, + false, + Some(140), + false, + false, + TerminalUrl::No, + ); + let handler = Handler::with_emitter(Box::new(emitter)); + (handler, source_map, output) +} + +/// Returns the result of parsing the given string via the given callback. +/// +/// If there are any errors, this will panic. pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T where F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, @@ -32,6 +59,26 @@ where x } +/// Verifies that parsing the given string using the given callback will +/// generate an error that contains the given text. +pub(crate) fn with_expected_parse_error(source_str: &str, expected_output: &str, f: F) +where + F: for<'a> FnOnce(&mut Parser<'a>) -> PResult<'a, T>, +{ + let (handler, source_map, output) = create_test_handler(); + let ps = ParseSess::with_span_handler(handler, source_map); + let mut p = string_to_parser(&ps, source_str.to_string()); + let result = f(&mut p); + assert!(result.is_ok()); + + let bytes = output.lock().unwrap(); + let actual_output = str::from_utf8(&bytes).unwrap(); + println!("expected output:\n------\n{}------", expected_output); + println!("actual output:\n------\n{}------", actual_output); + + assert!(actual_output.contains(expected_output)) +} + /// Maps a string to tts, using a made-up filename. pub(crate) fn string_to_stream(source_str: String) -> TokenStream { let ps = ParseSess::new( @@ -130,13 +177,7 @@ impl Write for Shared { fn test_harness(file_text: &str, span_labels: Vec, expected_output: &str) { create_default_session_if_not_set_then(|_| { - let output = Arc::new(Mutex::new(Vec::new())); - - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], - false, - ); - let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let (handler, source_map, output) = create_test_handler(); source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned()); let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end); @@ -148,20 +189,6 @@ fn test_harness(file_text: &str, span_labels: Vec, expected_output: & println!("text: {:?}", source_map.span_to_snippet(span)); } - let emitter = EmitterWriter::new( - Box::new(Shared { data: output.clone() }), - Some(source_map.clone()), - None, - fallback_bundle, - false, - false, - false, - None, - false, - false, - TerminalUrl::No, - ); - let handler = Handler::with_emitter(true, None, Box::new(emitter), None); #[allow(rustc::untranslatable_diagnostic)] handler.span_err(msp, "foo"); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 2c92277b50d2..252177932e43 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -267,8 +267,6 @@ lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead lint_improper_ctypes_char_reason = the `char` type has no C equivalent lint_improper_ctypes_dyn = trait objects have no C equivalent -lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field - lint_improper_ctypes_enum_repr_help = consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 3379479b1742..226d01b79a89 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -985,39 +985,43 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ) -> FfiResult<'tcx> { use FfiResult::*; - let transparent_safety = def.repr().transparent().then(|| { - // Can assume that at most one field is not a ZST, so only check - // that field's type for FFI-safety. + let transparent_with_all_zst_fields = if def.repr().transparent() { if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { - return self.check_field_type_for_ffi(cache, field, args); + // Transparent newtypes have at most one non-ZST field which needs to be checked.. + match self.check_field_type_for_ffi(cache, field, args) { + FfiUnsafe { ty, .. } if ty.is_unit() => (), + r => return r, + } + + false } else { - // All fields are ZSTs; this means that the type should behave - // like (), which is FFI-unsafe... except if all fields are PhantomData, - // which is tested for below - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + true } - }); - // We can't completely trust repr(C) markings; make sure the fields are - // actually safe. + } else { + false + }; + + // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - match self.check_field_type_for_ffi(cache, &field, args) { - FfiSafe => { - all_phantom = false; - } - FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_enum_phantomdata, - help: None, - }; - } - FfiPhantom(..) => {} - r => return transparent_safety.unwrap_or(r), + all_phantom &= match self.check_field_type_for_ffi(cache, &field, args) { + FfiSafe => false, + // `()` fields are FFI-safe! + FfiUnsafe { ty, .. } if ty.is_unit() => false, + FfiPhantom(..) => true, + r @ FfiUnsafe { .. } => return r, } } - if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) } + if all_phantom { + FfiPhantom(ty) + } else if transparent_with_all_zst_fields { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } + } else { + FfiSafe + } } /// Checks if the given type is "ffi-safe" (has a stable, well-defined @@ -1220,25 +1224,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } let sig = tcx.erase_late_bound_regions(sig); - if !sig.output().is_unit() { - let r = self.check_type_for_ffi(cache, sig.output()); - match r { - FfiSafe => {} - _ => { - return r; - } - } - } for arg in sig.inputs() { - let r = self.check_type_for_ffi(cache, *arg); - match r { + match self.check_type_for_ffi(cache, *arg) { FfiSafe => {} - _ => { - return r; - } + r => return r, } } - FfiSafe + + let ret_ty = sig.output(); + if ret_ty.is_unit() { + return FfiSafe; + } + + self.check_type_for_ffi(cache, ret_ty) } ty::Foreign(..) => FfiSafe, @@ -1354,7 +1352,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } // Don't report FFI errors for unit return types. This check exists here, and not in - // `check_foreign_fn` (where it would make more sense) so that normalization has definitely + // the caller (where it would make more sense) so that normalization has definitely // happened. if is_return_type && ty.is_unit() { return; @@ -1370,9 +1368,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { None, ); } - // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic - // argument, which after substitution, is `()`, then this branch can be hit. - FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {} FfiResult::FfiUnsafe { ty, reason, help } => { self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); } diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 4c238308fe87..bb8e774cea3d 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -13,7 +13,7 @@ gsgdt = "0.1.2" field-offset = "0.3.5" measureme = "10.0.0" polonius-engine = "0.13.0" -rustc_apfloat = { path = "../rustc_apfloat" } +rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_attr = { path = "../rustc_attr" } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 58449ee9eb48..c7e2c625ce57 100644 --- a/compiler/rustc_mir_build/Cargo.toml +++ b/compiler/rustc_mir_build/Cargo.toml @@ -10,7 +10,7 @@ rustc_arena = { path = "../rustc_arena" } tracing = "0.1" either = "1" rustc_middle = { path = "../rustc_middle" } -rustc_apfloat = { path = "../rustc_apfloat" } +rustc_apfloat = "0.2.0" rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 01b945afda63..ac07c25763ba 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -494,7 +494,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { trace!("assertion on {:?} should be {:?}", value, expected); let expected = Scalar::from_bool(expected); - let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(&value))?; + let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(value))?; if expected != value_const { // Poison all places this operand references so that further code @@ -664,7 +664,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { } TerminatorKind::SwitchInt { ref discr, ref targets } => { if let Some(ref value) = self.eval_operand(&discr, location) - && let Some(value_const) = self.use_ecx(location, |this| this.ecx.read_scalar(&value)) + && let Some(value_const) = self.use_ecx(location, |this| this.ecx.read_scalar(value)) && let Ok(constant) = value_const.try_to_int() && let Ok(constant) = constant.to_bits(constant.size()) { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c19c2d1dc9e2..4a535f80b8ac 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1433,8 +1433,6 @@ options! { dep_tasks: bool = (false, parse_bool, [UNTRACKED], "print tasks that execute and the color their dep node gets (requires debug build) \ (default: no)"), - diagnostic_width: Option = (None, parse_opt_number, [UNTRACKED], - "set the current output width for diagnostic truncation"), dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED], "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \ (default: no)"), diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index b0a67c564ce1..bca49981668e 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -9,7 +9,7 @@ use crate::lint::{ use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::sync::{AppendOnlyVec, AtomicBool, Lock, Lrc}; -use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler}; +use rustc_errors::{emitter::SilentEmitter, Handler}; use rustc_errors::{ fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, Noted, StashKey, @@ -224,15 +224,7 @@ impl ParseSess { pub fn new(locale_resources: Vec<&'static str>, file_path_mapping: FilePathMapping) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); let sm = Lrc::new(SourceMap::new(file_path_mapping)); - let handler = Handler::with_tty_emitter( - ColorConfig::Auto, - true, - None, - Some(sm.clone()), - None, - fallback_bundle, - None, - ); + let handler = Handler::with_tty_emitter(Some(sm.clone()), fallback_bundle); ParseSess::with_span_handler(handler, sm) } @@ -262,21 +254,9 @@ impl ParseSess { pub fn with_silent_emitter(fatal_note: Option) -> Self { let fallback_bundle = fallback_fluent_bundle(Vec::new(), false); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fatal_handler = Handler::with_tty_emitter( - ColorConfig::Auto, - false, - None, - None, - None, - fallback_bundle, - None, - ); - let handler = Handler::with_emitter( - false, - None, - Box::new(SilentEmitter { fatal_handler, fatal_note }), - None, - ); + let fatal_handler = Handler::with_tty_emitter(None, fallback_bundle).disable_warnings(); + let handler = Handler::with_emitter(Box::new(SilentEmitter { fatal_handler, fatal_note })) + .disable_warnings(); ParseSess::with_span_handler(handler, sm) } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3c8be439111d..ad22e7c703d7 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1442,11 +1442,11 @@ pub fn build_session( ); let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle); - let span_diagnostic = rustc_errors::Handler::with_emitter_and_flags( - emitter, - sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings), - ice_file, - ); + let mut span_diagnostic = rustc_errors::Handler::with_emitter(emitter) + .with_flags(sopts.unstable_opts.diagnostic_handler_flags(can_emit_warnings)); + if let Some(ice_file) = ice_file { + span_diagnostic = span_diagnostic.with_ice_file(ice_file); + } let self_profiler = if let SwitchWithOptPath::Enabled(ref d) = sopts.unstable_opts.self_profile { @@ -1737,7 +1737,7 @@ pub struct EarlyErrorHandler { impl EarlyErrorHandler { pub fn new(output: ErrorOutputType) -> Self { let emitter = mk_emitter(output); - Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter, None) } + Self { handler: rustc_errors::Handler::with_emitter(emitter) } } pub fn abort_if_errors(&self) { @@ -1751,7 +1751,7 @@ impl EarlyErrorHandler { self.handler.abort_if_errors(); let emitter = mk_emitter(output); - self.handler = Handler::with_emitter(true, None, emitter, None); + self.handler = Handler::with_emitter(emitter); } #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 044e2f8f3256..cefcab1e18f5 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -825,8 +825,10 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { ty::Alias(alias_kind, alias_ty) => { TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables)) } - ty::Param(_) => todo!(), - ty::Bound(_, _) => todo!(), + ty::Param(param_ty) => TyKind::Param(param_ty.stable(tables)), + ty::Bound(debruijn_idx, bound_ty) => { + TyKind::Bound(debruijn_idx.as_usize(), bound_ty.stable(tables)) + } ty::Placeholder(..) | ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) @@ -837,3 +839,19 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { } } } + +impl<'tcx> Stable<'tcx> for rustc_middle::ty::ParamTy { + type T = stable_mir::ty::ParamTy; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::ParamTy; + ParamTy { index: self.index, name: self.name.to_string() } + } +} + +impl<'tcx> Stable<'tcx> for rustc_middle::ty::BoundTy { + type T = stable_mir::ty::BoundTy; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundTy; + BoundTy { var: self.var.as_usize(), kind: self.kind.stable(tables) } + } +} diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 7b4747a7fe29..7a72afd666cf 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -18,6 +18,8 @@ type Span = Opaque; pub enum TyKind { RigidTy(RigidTy), Alias(AliasKind, AliasTy), + Param(ParamTy), + Bound(usize, BoundTy), } #[derive(Clone, Debug)] @@ -228,3 +230,15 @@ pub struct ExistentialProjection { pub generic_args: GenericArgs, pub term: TermKind, } + +#[derive(Clone, Debug)] +pub struct ParamTy { + pub index: u32, + pub name: String, +} + +#[derive(Clone, Debug)] +pub struct BoundTy { + pub var: usize, + pub kind: BoundTyKind, +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 08925761b393..54eb7bef5f20 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -372,6 +372,7 @@ symbols! { arm_target_feature, array, arrays, + as_mut_ptr, as_ptr, as_ref, as_str, @@ -858,6 +859,7 @@ symbols! { item, item_like_imports, iter, + iter_mut, iter_repeat, iterator_collect_fn, kcfi, diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index f683832e3df0..845b57911618 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -7,10 +7,10 @@ /// /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. -use core::fmt::Display; use rustc_data_structures::base_n; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; +use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, UintTy, @@ -19,6 +19,7 @@ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::DefId; use rustc_span::sym; use rustc_target::abi::call::{Conv, FnAbi}; +use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; use std::fmt::Write as _; @@ -93,44 +94,54 @@ fn encode_const<'tcx>( dict: &mut FxHashMap, usize>, options: EncodeTyOptions, ) -> String { - // L[n]E as literal argument + // L[n][]E as literal argument let mut s = String::from('L'); - // Element type - s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + match c.kind() { + // Const parameters + ty::ConstKind::Param(..) => { + // LE as literal argument - // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16, - // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1. - fn push_signed_value(s: &mut String, value: T, zero: T) { - if value < zero { - s.push('n') - }; - let _ = write!(s, "{value}"); - } + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + } - fn push_unsigned_value(s: &mut String, value: T) { - let _ = write!(s, "{value}"); - } + // Literal arguments + ty::ConstKind::Value(..) => { + // L[n]E as literal argument - if let Some(scalar_int) = c.try_to_scalar_int() { - let signed = c.ty().is_signed(); - match scalar_int.size().bits() { - 8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0), - 16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0), - 32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0), - 64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0), - 128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0), - 8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()), - 16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()), - 32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()), - 64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()), - 128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()), - _ => { - bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits()); + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + + // The only allowed types of const values are bool, u8, u16, u32, + // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The + // bool value false is encoded as 0 and true as 1. + match c.ty().kind() { + ty::Int(ity) => { + let bits = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty()); + let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; + if val < 0 { + s.push('n'); + } + let _ = write!(s, "{val}"); + } + ty::Uint(_) => { + let val = c.eval_bits(tcx, ty::ParamEnv::reveal_all(), c.ty()); + let _ = write!(s, "{val}"); + } + ty::Bool => { + let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + let _ = write!(s, "{val}"); + } + _ => { + bug!("encode_const: unexpected type `{:?}`", c.ty()); + } } - }; - } else { - bug!("encode_const: unexpected type `{:?}`", c.ty()); + } + + _ => { + bug!("encode_const: unexpected kind `{:?}`", c.kind()); + } } // Close the "L..E" pair diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 092b097c3961..22a1c09782ce 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -131,6 +131,17 @@ use crate::intrinsics; use crate::hint::spin_loop; +// Some architectures don't have byte-sized atomics, which results in LLVM +// emulating them using a LL/SC loop. However for AtomicBool we can take +// advantage of the fact that it only ever contains 0 or 1 and use atomic OR/AND +// instead, which LLVM can emulate using a larger atomic OR/AND operation. +// +// This list should only contain architectures which have word-sized atomic-or/ +// atomic-and instructions but don't natively support byte-sized atomics. +#[cfg(target_has_atomic = "8")] +const EMULATE_ATOMIC_BOOL: bool = + cfg!(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "loongarch64")); + /// A boolean type which can be safely shared between threads. /// /// This type has the same in-memory representation as a [`bool`]. @@ -553,8 +564,12 @@ impl AtomicBool { #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn swap(&self, val: bool, order: Ordering) -> bool { - // SAFETY: data races are prevented by atomic intrinsics. - unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } + if EMULATE_ATOMIC_BOOL { + if val { self.fetch_or(true, order) } else { self.fetch_and(false, order) } + } else { + // SAFETY: data races are prevented by atomic intrinsics. + unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } + } } /// Stores a value into the [`bool`] if the current value is the same as the `current` value. @@ -664,12 +679,39 @@ impl AtomicBool { success: Ordering, failure: Ordering, ) -> Result { - // SAFETY: data races are prevented by atomic intrinsics. - match unsafe { - atomic_compare_exchange(self.v.get(), current as u8, new as u8, success, failure) - } { - Ok(x) => Ok(x != 0), - Err(x) => Err(x != 0), + if EMULATE_ATOMIC_BOOL { + // Pick the strongest ordering from success and failure. + let order = match (success, failure) { + (SeqCst, _) => SeqCst, + (_, SeqCst) => SeqCst, + (AcqRel, _) => AcqRel, + (_, AcqRel) => { + panic!("there is no such thing as an acquire-release failure ordering") + } + (Release, Acquire) => AcqRel, + (Acquire, _) => Acquire, + (_, Acquire) => Acquire, + (Release, Relaxed) => Release, + (_, Release) => panic!("there is no such thing as a release failure ordering"), + (Relaxed, Relaxed) => Relaxed, + }; + let old = if current == new { + // This is a no-op, but we still need to perform the operation + // for memory ordering reasons. + self.fetch_or(false, order) + } else { + // This sets the value to the new one and returns the old one. + self.swap(new, order) + }; + if old == current { Ok(old) } else { Err(old) } + } else { + // SAFETY: data races are prevented by atomic intrinsics. + match unsafe { + atomic_compare_exchange(self.v.get(), current as u8, new as u8, success, failure) + } { + Ok(x) => Ok(x != 0), + Err(x) => Err(x != 0), + } } } @@ -719,6 +761,10 @@ impl AtomicBool { success: Ordering, failure: Ordering, ) -> Result { + if EMULATE_ATOMIC_BOOL { + return self.compare_exchange(current, new, success, failure); + } + // SAFETY: data races are prevented by atomic intrinsics. match unsafe { atomic_compare_exchange_weak(self.v.get(), current as u8, new as u8, success, failure) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 9ff01b9c35d1..d74f0f00e46f 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -957,6 +957,7 @@ fn readlink_not_symlink() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks fn links_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); @@ -1453,6 +1454,7 @@ fn metadata_access_times() { /// Test creating hard links to symlinks. #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating hardlinks fn symlink_hard_link() { let tmpdir = tmpdir(); if !got_symlink_permission(&tmpdir) { diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 39f10c50dc42..3d4302e665c2 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -23,6 +23,7 @@ macro_rules! or_panic { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn basic() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -93,6 +94,7 @@ fn pair() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn try_clone() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -119,6 +121,7 @@ fn try_clone() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn iter() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -168,6 +171,7 @@ fn long_path() { #[test] #[cfg(not(target_os = "nto"))] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn timeouts() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -195,6 +199,7 @@ fn timeouts() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_read_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -214,6 +219,7 @@ fn test_read_timeout() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_read_with_timeout() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -241,6 +247,7 @@ fn test_read_with_timeout() { // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors // when passed zero Durations #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_stream_timeout_zero_duration() { let dir = tmpdir(); let socket_path = dir.path().join("sock"); @@ -260,6 +267,7 @@ fn test_unix_stream_timeout_zero_duration() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -276,6 +284,7 @@ fn test_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unnamed_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -293,6 +302,7 @@ fn test_unnamed_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_connect_to_recv_addr() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -317,6 +327,7 @@ fn test_unix_datagram_connect_to_recv_addr() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_connect_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -343,6 +354,7 @@ fn test_connect_unix_datagram() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_recv() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); @@ -385,6 +397,7 @@ fn datagram_pair() { // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors // when passed zero Durations #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_timeout_zero_duration() { let dir = tmpdir(); let path = dir.path().join("sock"); @@ -529,6 +542,7 @@ fn test_abstract_no_pathname_and_not_unnamed() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_stream_peek() { let (txdone, rxdone) = crate::sync::mpsc::channel(); @@ -561,6 +575,7 @@ fn test_unix_stream_peek() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_peek() { let dir = tmpdir(); let path1 = dir.path().join("sock"); @@ -585,6 +600,7 @@ fn test_unix_datagram_peek() { } #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_unix_datagram_peek_from() { let dir = tmpdir(); let path1 = dir.path().join("sock"); @@ -648,6 +664,7 @@ fn test_send_vectored_fds_unix_stream() { #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_to_unix_datagram() { fn getpid() -> libc::pid_t { unsafe { libc::getpid() } @@ -715,6 +732,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] +#[cfg_attr(target_os = "android", ignore)] // Android SELinux rules prevent creating Unix sockets fn test_send_vectored_with_ancillary_unix_datagram() { let dir = tmpdir(); let path1 = dir.path().join("sock1"); diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index d471be33ed55..fbf158f56fcc 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -17,7 +17,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { not(target_os = "tvos"), not(target_os = "watchos"), not(target_os = "openbsd"), - not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "fuchsia"), not(target_os = "redox"), @@ -68,11 +67,25 @@ mod imp { unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } } + #[cfg(target_os = "freebsd")] + fn getrandom(buf: &mut [u8]) -> libc::ssize_t { + // FIXME: using the above when libary std's libc is updated + extern "C" { + fn getrandom( + buffer: *mut libc::c_void, + length: libc::size_t, + flags: libc::c_uint, + ) -> libc::ssize_t; + } + unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) } + } + #[cfg(not(any( target_os = "linux", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "freebsd" )))] fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false @@ -82,7 +95,8 @@ mod imp { target_os = "linux", target_os = "android", target_os = "espidf", - target_os = "horizon" + target_os = "horizon", + target_os = "freebsd" ))] fn getrandom_fill_bytes(v: &mut [u8]) -> bool { use crate::sync::atomic::{AtomicBool, Ordering}; @@ -222,7 +236,7 @@ mod imp { } } -#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] +#[cfg(target_os = "netbsd")] mod imp { use crate::ptr; diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp index 120b3c9c4d28..ffc38057900a 100644 --- a/src/bootstrap/download-ci-llvm-stamp +++ b/src/bootstrap/download-ci-llvm-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-llvm` configuration download a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/112931 +Last change is for: https://github.com/rust-lang/rust/pull/113996 diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index 07719a711788..02fef4b3e830 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -559,6 +559,8 @@ fn configure_cmake( if target.contains("netbsd") { cfg.define("CMAKE_SYSTEM_NAME", "NetBSD"); + } else if target.contains("dragonfly") { + cfg.define("CMAKE_SYSTEM_NAME", "DragonFly"); } else if target.contains("freebsd") { cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD"); } else if target.contains("windows") { @@ -569,7 +571,12 @@ fn configure_cmake( cfg.define("CMAKE_SYSTEM_NAME", "SunOS"); } else if target.contains("linux") { cfg.define("CMAKE_SYSTEM_NAME", "Linux"); + } else { + builder.info( + "could not determine CMAKE_SYSTEM_NAME from the target `{target}`, build may fail", + ); } + // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in // that case like CMake we cannot easily determine system version either. // diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index c69b21488d23..1fe92098fd6a 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -430,6 +430,10 @@ impl Step for Rustfmt { &[], ); + if builder.config.cmd.bless() { + cargo.env("BLESS", "1"); + } + let dir = testdir(builder, compiler.host); t!(fs::create_dir_all(&dir)); cargo.env("RUSTFMT_TEST_DIR", dir); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 89eaf561e6a5..1c4149fd545e 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -141,6 +141,7 @@ pub(crate) fn try_inline_glob( current_mod: LocalDefId, visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, + import: &hir::Item<'_>, ) -> Option> { let did = res.opt_def_id()?; if did.is_local() { @@ -158,7 +159,15 @@ pub(crate) fn try_inline_glob( .filter(|child| !child.reexport_chain.is_empty()) .filter_map(|child| child.res.opt_def_id()) .collect(); - let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports)); + let attrs = cx.tcx.hir().attrs(import.hir_id()); + let mut items = build_module_items( + cx, + did, + visited, + inlined_names, + Some(&reexports), + Some((attrs, Some(import.owner_id.def_id.to_def_id()))), + ); items.retain(|item| { if let Some(name) = item.name { // If an item with the same type and name already exists, @@ -549,7 +558,7 @@ pub(crate) fn build_impl( } fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module { - let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None); + let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None, None); let span = clean::Span::new(cx.tcx.def_span(did)); clean::Module { items, span } @@ -561,6 +570,7 @@ fn build_module_items( visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, allowed_def_ids: Option<&DefIdSet>, + attrs: Option<(&[ast::Attribute], Option)>, ) -> Vec { let mut items = Vec::new(); @@ -615,7 +625,7 @@ fn build_module_items( cfg: None, inline_stmt_id: None, }); - } else if let Some(i) = try_inline(cx, res, item.ident.name, None, visited) { + } else if let Some(i) = try_inline(cx, res, item.ident.name, attrs, visited) { items.extend(i) } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 440874df14cf..a57854837d54 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -132,25 +132,31 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< }); let kind = ModuleItem(Module { items, span }); - generate_item_with_correct_attrs(cx, kind, doc.def_id, doc.name, doc.import_id, doc.renamed) + generate_item_with_correct_attrs( + cx, + kind, + doc.def_id.to_def_id(), + doc.name, + doc.import_id, + doc.renamed, + ) } fn generate_item_with_correct_attrs( cx: &mut DocContext<'_>, kind: ItemKind, - local_def_id: LocalDefId, + def_id: DefId, name: Symbol, import_id: Option, renamed: Option, ) -> Item { - let def_id = local_def_id.to_def_id(); let target_attrs = inline::load_attrs(cx, def_id); let attrs = if let Some(import_id) = import_id { let is_inline = inline::load_attrs(cx, import_id.to_def_id()) .lists(sym::doc) .get_word_attr(sym::inline) .is_some(); - let mut attrs = get_all_import_attributes(cx, import_id, local_def_id, is_inline); + let mut attrs = get_all_import_attributes(cx, import_id, def_id, is_inline); add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None); attrs } else { @@ -2308,10 +2314,10 @@ fn clean_bare_fn_ty<'tcx>( pub(crate) fn reexport_chain<'tcx>( tcx: TyCtxt<'tcx>, import_def_id: LocalDefId, - target_def_id: LocalDefId, + target_def_id: DefId, ) -> &'tcx [Reexport] { for child in tcx.module_children_local(tcx.local_parent(import_def_id)) { - if child.res.opt_def_id() == Some(target_def_id.to_def_id()) + if child.res.opt_def_id() == Some(target_def_id) && child.reexport_chain.first().and_then(|r| r.id()) == Some(import_def_id.to_def_id()) { return &child.reexport_chain; @@ -2324,7 +2330,7 @@ pub(crate) fn reexport_chain<'tcx>( fn get_all_import_attributes<'hir>( cx: &mut DocContext<'hir>, import_def_id: LocalDefId, - target_def_id: LocalDefId, + target_def_id: DefId, is_inline: bool, ) -> Vec<(Cow<'hir, ast::Attribute>, Option)> { let mut attrs = Vec::new(); @@ -2541,7 +2547,7 @@ fn clean_maybe_renamed_item<'tcx>( vec![generate_item_with_correct_attrs( cx, kind, - item.owner_id.def_id, + item.owner_id.def_id.to_def_id(), name, import_id, renamed, @@ -2691,6 +2697,7 @@ fn clean_use_statement_inner<'tcx>( let inline_attr = attrs.lists(sym::doc).get_word_attr(sym::inline); let pub_underscore = visibility.is_public() && name == kw::Underscore; let current_mod = cx.tcx.parent_module_from_def_id(import.owner_id.def_id); + let import_def_id = import.owner_id.def_id.to_def_id(); // The parent of the module in which this import resides. This // is the same as `current_mod` if that's already the top @@ -2741,9 +2748,14 @@ fn clean_use_statement_inner<'tcx>( let inner = if kind == hir::UseKind::Glob { if !denied { let mut visited = DefIdSet::default(); - if let Some(items) = - inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names) - { + if let Some(items) = inline::try_inline_glob( + cx, + path.res, + current_mod, + &mut visited, + inlined_names, + import, + ) { return items; } } @@ -2759,7 +2771,6 @@ fn clean_use_statement_inner<'tcx>( denied = true; } if !denied { - let import_def_id = import.owner_id.to_def_id(); if let Some(mut items) = inline::try_inline( cx, path.res, @@ -2779,7 +2790,7 @@ fn clean_use_statement_inner<'tcx>( Import::new_simple(name, resolve_use_source(cx, path), true) }; - vec![Item::from_def_id_and_parts(import.owner_id.to_def_id(), None, ImportItem(inner), cx)] + vec![Item::from_def_id_and_parts(import_def_id, None, ImportItem(inner), cx)] } fn clean_maybe_renamed_foreign_item<'tcx>( diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7fb069d6e707..6f791aec3f26 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -173,11 +173,8 @@ pub(crate) fn new_handler( } }; - rustc_errors::Handler::with_emitter_and_flags( - emitter, - unstable_opts.diagnostic_handler_flags(true), - None, - ) + rustc_errors::Handler::with_emitter(emitter) + .with_flags(unstable_opts.diagnostic_handler_flags(true)) } /// Parse, resolve, and typecheck the given crate. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 6766dcba18af..a48273a5c73f 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -587,7 +587,7 @@ pub(crate) fn make_test( ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser - let handler = Handler::with_emitter(false, None, Box::new(emitter), None); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); let mut found_main = false; @@ -774,7 +774,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { TerminalUrl::No, ); - let handler = Handler::with_emitter(false, None, Box::new(emitter), None); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) { diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index c82f2bc987aa..8e5ee382c86d 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -40,7 +40,7 @@ fn check_rust_syntax( let emitter = BufferEmitter { buffer: Lrc::clone(&buffer), fallback_bundle }; let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let handler = Handler::with_emitter(false, None, Box::new(emitter), None); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let source = dox[code_block.code].to_owned(); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 66737a015215..2f67e12df919 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -282,7 +282,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // made reachable by cross-crate inlining which we're checking here. // (this is done here because we need to know this upfront). crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did); - if is_hidden { + if is_hidden || glob { return false; } // We store inlined foreign items otherwise, it'd mean that the `use` item would be kept @@ -376,7 +376,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { return true; } let tcx = self.cx.tcx; - let item_def_id = reexport_chain(tcx, import_def_id, target_def_id) + let item_def_id = reexport_chain(tcx, import_def_id, target_def_id.to_def_id()) .iter() .flat_map(|reexport| reexport.id()) .map(|id| id.expect_local()) diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 8879c529262b..00f70e586f42 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -729,7 +729,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { false, TerminalUrl::No, ); - let handler = Handler::with_emitter(false, None, Box::new(emitter), None); + let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs index fef90f6eba49..f490a7175540 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = filter_recv.kind { let p = path.ident.name; - if p == sym::iter || p == sym!(iter_mut) { + if p == sym::iter || p == sym::iter_mut { receiver } else { filter_recv diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 84ef27f73659..37125337d6f0 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -472,7 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { // This is fine with StackedBorrow and race checks because they don't concern metadata on // the *value* (including the associated provenance if this is an AtomicPtr) at this location. // Only metadata on the location itself is used. - let scalar = this.allow_data_races_ref(move |this| this.read_scalar(&place.into()))?; + let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?; this.validate_overlapping_atomic(place)?; this.buffered_atomic_read(place, atomic, scalar, || { this.validate_atomic_load(place, atomic) @@ -490,7 +490,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { this.atomic_access_check(dest)?; this.validate_overlapping_atomic(dest)?; - this.allow_data_races_mut(move |this| this.write_scalar(val, &dest.into()))?; + this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; this.validate_atomic_store(dest, atomic)?; // FIXME: it's not possible to get the value before write_scalar. A read_scalar will cause // side effects from a read the program did not perform. So we have to initialise @@ -513,12 +513,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { this.atomic_access_check(place)?; this.validate_overlapping_atomic(place)?; - let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?; + let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; // Atomics wrap around on overflow. let val = this.binary_op(op, &old, rhs)?; let val = if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val }; - this.allow_data_races_mut(|this| this.write_immediate(*val, &place.into()))?; + this.allow_data_races_mut(|this| this.write_immediate(*val, place))?; this.validate_atomic_rmw(place, atomic)?; @@ -538,8 +538,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { this.atomic_access_check(place)?; this.validate_overlapping_atomic(place)?; - let old = this.allow_data_races_mut(|this| this.read_scalar(&place.into()))?; - this.allow_data_races_mut(|this| this.write_scalar(new, &place.into()))?; + let old = this.allow_data_races_mut(|this| this.read_scalar(place))?; + this.allow_data_races_mut(|this| this.write_scalar(new, place))?; this.validate_atomic_rmw(place, atomic)?; @@ -560,7 +560,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { this.atomic_access_check(place)?; this.validate_overlapping_atomic(place)?; - let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?; + let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?; let new_val = if min { @@ -569,7 +569,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { if lt { &rhs } else { &old } }; - this.allow_data_races_mut(|this| this.write_immediate(**new_val, &place.into()))?; + this.allow_data_races_mut(|this| this.write_immediate(**new_val, place))?; this.validate_atomic_rmw(place, atomic)?; @@ -603,7 +603,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { // to read with the failure ordering and if successful then try again with the success // read ordering and write in the success case. // Read as immediate for the sake of `binary_op()` - let old = this.allow_data_races_mut(|this| this.read_immediate(&(place.into())))?; + let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; // `binary_op` will bail if either of them is not a scalar. let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?; // If the operation would succeed, but is "weak", fail some portion @@ -621,7 +621,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { // if successful, perform a full rw-atomic validation // otherwise treat this as an atomic load with the fail ordering. if cmpxchg_success { - this.allow_data_races_mut(|this| this.write_scalar(new, &place.into()))?; + this.allow_data_races_mut(|this| this.write_scalar(new, place))?; this.validate_atomic_rmw(place, success)?; this.buffered_atomic_rmw(new, place, success, old.to_scalar())?; } else { diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 25c8df43ee26..9c11ad85aefd 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -834,7 +834,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let Some(thread_info_place) = thread { this.write_scalar( Scalar::from_uint(new_thread_id.to_u32(), thread_info_place.layout.size), - &thread_info_place.into(), + &thread_info_place, )?; } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 4f7b70649a93..b761a6cf4755 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -246,7 +246,7 @@ impl MainThreadState { this.machine.main_fn_ret_place.unwrap().ptr, this.machine.layouts.isize, ); - let exit_code = this.read_target_isize(&ret_place.into())?; + let exit_code = this.read_target_isize(&ret_place)?; // Need to call this ourselves since we are not going to return to the scheduler // loop, and we want the main thread TLS to not show up as memory leaks. this.terminate_active_thread()?; @@ -321,7 +321,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { let place = ecx.project_field(&argvs_place, idx)?; - ecx.write_immediate(arg, &place.into())?; + ecx.write_immediate(arg, &place)?; } ecx.mark_immutable(&argvs_place); // A pointer to that place is the 3rd argument for main. @@ -330,7 +330,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( { let argc_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?; - ecx.write_scalar(argc, &argc_place.into())?; + ecx.write_scalar(argc, &argc_place)?; ecx.mark_immutable(&argc_place); ecx.machine.argc = Some(*argc_place); @@ -338,7 +338,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, MiriMemoryKind::Machine.into(), )?; - ecx.write_immediate(argv, &argv_place.into())?; + ecx.write_immediate(argv, &argv_place)?; ecx.mark_immutable(&argv_place); ecx.machine.argv = Some(*argv_place); } @@ -355,7 +355,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( // Store the UTF-16 string. We just allocated so we know the bounds are fine. for (idx, &c) in cmd_utf16.iter().enumerate() { let place = ecx.project_field(&cmd_place, idx)?; - ecx.write_scalar(Scalar::from_u16(c), &place.into())?; + ecx.write_scalar(Scalar::from_u16(c), &place)?; } ecx.mark_immutable(&cmd_place); } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index f6a438f5d629..d41bcc978b0c 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -166,7 +166,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let const_val = this.eval_global(cid, None).unwrap_or_else(|err| { panic!("failed to evaluate required Rust item: {path:?}\n{err:?}") }); - this.read_scalar(&const_val.into()) + this.read_scalar(&const_val) .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}")) } @@ -231,7 +231,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Project to the given *named* field (which must be a struct or union type). - fn project_field_named>( + fn project_field_named>( &self, base: &P, name: &str, @@ -252,13 +252,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_int( &mut self, i: impl Into, - dest: &PlaceTy<'tcx, Provenance>, + dest: &impl Writeable<'tcx, Provenance>, ) -> InterpResult<'tcx> { - assert!(dest.layout.abi.is_scalar(), "write_int on non-scalar type {}", dest.layout.ty); - let val = if dest.layout.abi.is_signed() { - Scalar::from_int(i, dest.layout.size) + assert!(dest.layout().abi.is_scalar(), "write_int on non-scalar type {}", dest.layout().ty); + let val = if dest.layout().abi.is_signed() { + Scalar::from_int(i, dest.layout().size) } else { - Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout.size) + Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout().size) }; self.eval_context_mut().write_scalar(val, dest) } @@ -267,12 +267,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_int_fields( &mut self, values: &[i128], - dest: &MPlaceTy<'tcx, Provenance>, + dest: &impl Writeable<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); for (idx, &val) in values.iter().enumerate() { let field = this.project_field(dest, idx)?; - this.write_int(val, &field.into())?; + this.write_int(val, &field)?; } Ok(()) } @@ -281,18 +281,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn write_int_fields_named( &mut self, values: &[(&str, i128)], - dest: &MPlaceTy<'tcx, Provenance>, + dest: &impl Writeable<'tcx, Provenance>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); for &(name, val) in values.iter() { let field = this.project_field_named(dest, name)?; - this.write_int(val, &field.into())?; + this.write_int(val, &field)?; } Ok(()) } /// Write a 0 of the appropriate size to `dest`. - fn write_null(&mut self, dest: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> { + fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> { self.write_int(0, dest) } @@ -600,14 +600,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// necessary. fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> { let this = self.eval_context_mut(); - if let Some(errno_place) = this.active_thread_ref().last_error { - Ok(errno_place) + if let Some(errno_place) = this.active_thread_ref().last_error.as_ref() { + Ok(errno_place.clone()) } else { // Allocate new place, set initial value to 0. let errno_layout = this.machine.layouts.u32; let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into())?; - this.write_scalar(Scalar::from_u32(0), &errno_place.into())?; - this.active_thread_mut().last_error = Some(errno_place); + this.write_scalar(Scalar::from_u32(0), &errno_place)?; + this.active_thread_mut().last_error = Some(errno_place.clone()); Ok(errno_place) } } @@ -616,14 +616,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let errno_place = this.last_error_place()?; - this.write_scalar(scalar, &errno_place.into()) + this.write_scalar(scalar, &errno_place) } /// Gets the last error variable. fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); let errno_place = this.last_error_place()?; - this.read_scalar(&errno_place.into()) + this.read_scalar(&errno_place) } /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` @@ -725,7 +725,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mplace = MPlaceTy::from_aligned_ptr(ptr, layout); - this.check_mplace(mplace)?; + this.check_mplace(&mplace)?; Ok(mplace) } @@ -772,7 +772,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?; - this.read_scalar(&value_place.into()) + this.read_scalar(&value_place) } fn write_scalar_at_offset( @@ -785,7 +785,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?; - this.write_scalar(value, &value_place.into()) + this.write_scalar(value, &value_place) } /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None` @@ -797,10 +797,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); let seconds_place = this.project_field(tp, 0)?; - let seconds_scalar = this.read_scalar(&seconds_place.into())?; + let seconds_scalar = this.read_scalar(&seconds_place)?; let seconds = seconds_scalar.to_target_isize(this)?; let nanoseconds_place = this.project_field(tp, 1)?; - let nanoseconds_scalar = this.read_scalar(&nanoseconds_place.into())?; + let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?; let nanoseconds = nanoseconds_scalar.to_target_isize(this)?; Ok(try { diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index a8f89d56f6d9..0c9c072b0511 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -651,7 +651,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { val: ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?; - this.write_immediate(*val, &place.into())?; + this.write_immediate(*val, &place)?; Self::add_extern_static(this, name, place.ptr); Ok(()) } @@ -668,7 +668,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { Self::add_extern_static( this, "environ", - this.machine.env_vars.environ.unwrap().ptr, + this.machine.env_vars.environ.as_ref().unwrap().ptr, ); // A couple zero-initialized pointer-sized extern statics. // Most of them are for weak symbols, which we all set to null (indicating that the @@ -685,7 +685,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { Self::add_extern_static( this, "environ", - this.machine.env_vars.environ.unwrap().ptr, + this.machine.env_vars.environ.as_ref().unwrap().ptr, ); } "android" => { diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index e4aa7467cbe0..e89b2e01a39a 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -85,7 +85,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { for (i, ptr) in ptrs.into_iter().enumerate() { let place = this.project_index(&alloc, i as u64)?; - this.write_pointer(ptr, &place.into())?; + this.write_pointer(ptr, &place)?; } this.write_immediate( @@ -106,7 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let op_place = buf_place.offset(offset, ptr_layout, this)?; - this.write_pointer(ptr, &op_place.into())?; + this.write_pointer(ptr, &op_place)?; } } _ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags), @@ -196,33 +196,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_immediate( name_alloc.to_ref(this), - &this.project_field(&dest, 0)?.into(), + &this.project_field(&dest, 0)?, )?; this.write_immediate( filename_alloc.to_ref(this), - &this.project_field(&dest, 1)?.into(), + &this.project_field(&dest, 1)?, )?; } 1 => { this.write_scalar( Scalar::from_target_usize(name.len().try_into().unwrap(), this), - &this.project_field(&dest, 0)?.into(), + &this.project_field(&dest, 0)?, )?; this.write_scalar( Scalar::from_target_usize(filename.len().try_into().unwrap(), this), - &this.project_field(&dest, 1)?.into(), + &this.project_field(&dest, 1)?, )?; } _ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags), } - this.write_scalar(Scalar::from_u32(lineno), &this.project_field(&dest, 2)?.into())?; - this.write_scalar(Scalar::from_u32(colno), &this.project_field(&dest, 3)?.into())?; + this.write_scalar(Scalar::from_u32(lineno), &this.project_field(&dest, 2)?)?; + this.write_scalar(Scalar::from_u32(colno), &this.project_field(&dest, 3)?)?; // Support a 4-field struct for now - this is deprecated // and slated for removal. if num_fields == 5 { - this.write_pointer(fn_ptr, &this.project_field(&dest, 4)?.into())?; + this.write_pointer(fn_ptr, &this.project_field(&dest, 4)?)?; } Ok(()) diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index f98fd0431ae6..3694ea51da6c 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -87,8 +87,8 @@ impl<'tcx> EnvVars<'tcx> { ecx.deallocate_ptr(ptr, None, MiriMemoryKind::Runtime.into())?; } // Deallocate environ var list. - let environ = ecx.machine.env_vars.environ.unwrap(); - let old_vars_ptr = ecx.read_pointer(&environ.into())?; + let environ = ecx.machine.env_vars.environ.as_ref().unwrap(); + let old_vars_ptr = ecx.read_pointer(environ)?; ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; Ok(()) } @@ -431,8 +431,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn update_environ(&mut self) -> InterpResult<'tcx> { let this = self.eval_context_mut(); // Deallocate the old environ list, if any. - if let Some(environ) = this.machine.env_vars.environ { - let old_vars_ptr = this.read_pointer(&environ.into())?; + if let Some(environ) = this.machine.env_vars.environ.as_ref() { + let old_vars_ptr = this.read_pointer(environ)?; this.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; } else { // No `environ` allocated yet, let's do that. @@ -457,9 +457,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; for (idx, var) in vars.into_iter().enumerate() { let place = this.project_field(&vars_place, idx)?; - this.write_pointer(var, &place.into())?; + this.write_pointer(var, &place)?; } - this.write_pointer(vars_place.ptr, &this.machine.env_vars.environ.unwrap().into())?; + this.write_pointer(vars_place.ptr, &this.machine.env_vars.environ.clone().unwrap())?; Ok(()) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index c753eadbbad3..763fa9235d04 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -914,16 +914,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let x = this.read_scalar(x)?.to_f64()?; let exp = this.read_scalar(exp)?.to_i32()?; - // Saturating cast to i16. Even those are outside the valid exponent range so - // `scalbn` below will do its over/underflow handling. - let exp = if exp > i32::from(i16::MAX) { - i16::MAX - } else if exp < i32::from(i16::MIN) { - i16::MIN - } else { - exp.try_into().unwrap() - }; - let res = x.scalbn(exp); this.write_scalar(Scalar::from_f64(res), dest)?; } diff --git a/src/tools/miri/src/shims/intrinsics/mod.rs b/src/tools/miri/src/shims/intrinsics/mod.rs index ca2c1652dc19..26f0a6606573 100644 --- a/src/tools/miri/src/shims/intrinsics/mod.rs +++ b/src/tools/miri/src/shims/intrinsics/mod.rs @@ -97,12 +97,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "volatile_load" => { let [place] = check_arg_count(args)?; let place = this.deref_operand(place)?; - this.copy_op(&place.into(), dest, /*allow_transmute*/ false)?; + this.copy_op(&place, dest, /*allow_transmute*/ false)?; } "volatile_store" => { let [place, dest] = check_arg_count(args)?; let place = this.deref_operand(place)?; - this.copy_op(dest, &place.into(), /*allow_transmute*/ false)?; + this.copy_op(dest, &place, /*allow_transmute*/ false)?; } "write_bytes" | "volatile_set_memory" => { diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index bddfe58ee3cd..103feae4ae7a 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -57,7 +57,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; for i in 0..dest_len { - let op = this.read_immediate(&this.project_index(&op, i)?.into())?; + let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; let val = match which { Op::MirOp(mir_op) => this.unary_op(mir_op, &op)?.to_scalar(), @@ -104,7 +104,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } }; - this.write_scalar(val, &dest.into())?; + this.write_scalar(val, &dest)?; } } #[rustfmt::skip] @@ -172,8 +172,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; for i in 0..dest_len { - let left = this.read_immediate(&this.project_index(&left, i)?.into())?; - let right = this.read_immediate(&this.project_index(&right, i)?.into())?; + let left = this.read_immediate(&this.project_index(&left, i)?)?; + let right = this.read_immediate(&this.project_index(&right, i)?)?; let dest = this.project_index(&dest, i)?; let val = match which { Op::MirOp(mir_op) => { @@ -217,7 +217,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fmin_op(&left, &right)? } }; - this.write_scalar(val, &dest.into())?; + this.write_scalar(val, &dest)?; } } "fma" => { @@ -232,9 +232,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, c_len); for i in 0..dest_len { - let a = this.read_scalar(&this.project_index(&a, i)?.into())?; - let b = this.read_scalar(&this.project_index(&b, i)?.into())?; - let c = this.read_scalar(&this.project_index(&c, i)?.into())?; + let a = this.read_scalar(&this.project_index(&a, i)?)?; + let b = this.read_scalar(&this.project_index(&b, i)?)?; + let c = this.read_scalar(&this.project_index(&c, i)?)?; let dest = this.project_index(&dest, i)?; // Works for f32 and f64. @@ -258,7 +258,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Scalar::from_u64(res.to_bits()) } }; - this.write_scalar(val, &dest.into())?; + this.write_scalar(val, &dest)?; } } #[rustfmt::skip] @@ -295,13 +295,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // Initialize with first lane, then proceed with the rest. - let mut res = this.read_immediate(&this.project_index(&op, 0)?.into())?; + let mut res = this.read_immediate(&this.project_index(&op, 0)?)?; if matches!(which, Op::MirOpBool(_)) { // Convert to `bool` scalar. res = imm_from_bool(simd_element_to_bool(res)?); } for i in 1..op_len { - let op = this.read_immediate(&this.project_index(&op, i)?.into())?; + let op = this.read_immediate(&this.project_index(&op, i)?)?; res = match which { Op::MirOp(mir_op) => { this.binary_op(mir_op, &res, &op)? @@ -355,7 +355,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut res = init; for i in 0..op_len { - let op = this.read_immediate(&this.project_index(&op, i)?.into())?; + let op = this.read_immediate(&this.project_index(&op, i)?)?; res = this.binary_op(mir_op, &res, &op)?; } this.write_immediate(*res, dest)?; @@ -372,13 +372,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, no_len); for i in 0..dest_len { - let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?; - let yes = this.read_immediate(&this.project_index(&yes, i)?.into())?; - let no = this.read_immediate(&this.project_index(&no, i)?.into())?; + let mask = this.read_immediate(&this.project_index(&mask, i)?)?; + let yes = this.read_immediate(&this.project_index(&yes, i)?)?; + let no = this.read_immediate(&this.project_index(&no, i)?)?; let dest = this.project_index(&dest, i)?; let val = if simd_element_to_bool(mask)? { yes } else { no }; - this.write_immediate(*val, &dest.into())?; + this.write_immediate(*val, &dest)?; } } "select_bitmask" => { @@ -403,12 +403,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { & 1u64 .checked_shl(simd_bitmask_index(i, dest_len, this.data_layout().endian)) .unwrap(); - let yes = this.read_immediate(&this.project_index(&yes, i.into())?.into())?; - let no = this.read_immediate(&this.project_index(&no, i.into())?.into())?; + let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?; + let no = this.read_immediate(&this.project_index(&no, i.into())?)?; let dest = this.project_index(&dest, i.into())?; let val = if mask != 0 { yes } else { no }; - this.write_immediate(*val, &dest.into())?; + this.write_immediate(*val, &dest)?; } for i in dest_len..bitmask_len { // If the mask is "padded", ensure that padding is all-zero. @@ -435,7 +435,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let from_exposed_cast = intrinsic_name == "from_exposed_addr"; for i in 0..dest_len { - let op = this.read_immediate(&this.project_index(&op, i)?.into())?; + let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) { @@ -472,7 +472,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { to_ty = dest.layout.ty, ), }; - this.write_immediate(val, &dest.into())?; + this.write_immediate(val, &dest)?; } } "shuffle" => { @@ -503,17 +503,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let dest = this.project_index(&dest, i)?; let val = if src_index < left_len { - this.read_immediate(&this.project_index(&left, src_index)?.into())? + this.read_immediate(&this.project_index(&left, src_index)?)? } else if src_index < left_len.checked_add(right_len).unwrap() { let right_idx = src_index.checked_sub(left_len).unwrap(); - this.read_immediate(&this.project_index(&right, right_idx)?.into())? + this.read_immediate(&this.project_index(&right, right_idx)?)? } else { span_bug!( this.cur_span(), "simd_shuffle index {src_index} is out of bounds for 2 vectors of size {left_len}", ); }; - this.write_immediate(*val, &dest.into())?; + this.write_immediate(*val, &dest)?; } } "gather" => { @@ -528,19 +528,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, mask_len); for i in 0..dest_len { - let passthru = - this.read_immediate(&this.project_index(&passthru, i)?.into())?; - let ptr = this.read_immediate(&this.project_index(&ptrs, i)?.into())?; - let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?; + let passthru = this.read_immediate(&this.project_index(&passthru, i)?)?; + let ptr = this.read_immediate(&this.project_index(&ptrs, i)?)?; + let mask = this.read_immediate(&this.project_index(&mask, i)?)?; let dest = this.project_index(&dest, i)?; let val = if simd_element_to_bool(mask)? { - let place = this.deref_operand(&ptr.into())?; - this.read_immediate(&place.into())? + let place = this.deref_operand(&ptr)?; + this.read_immediate(&place)? } else { passthru }; - this.write_immediate(*val, &dest.into())?; + this.write_immediate(*val, &dest)?; } } "scatter" => { @@ -553,13 +552,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(ptrs_len, mask_len); for i in 0..ptrs_len { - let value = this.read_immediate(&this.project_index(&value, i)?.into())?; - let ptr = this.read_immediate(&this.project_index(&ptrs, i)?.into())?; - let mask = this.read_immediate(&this.project_index(&mask, i)?.into())?; + let value = this.read_immediate(&this.project_index(&value, i)?)?; + let ptr = this.read_immediate(&this.project_index(&ptrs, i)?)?; + let mask = this.read_immediate(&this.project_index(&mask, i)?)?; if simd_element_to_bool(mask)? { - let place = this.deref_operand(&ptr.into())?; - this.write_immediate(*value, &place.into())?; + let place = this.deref_operand(&ptr)?; + this.write_immediate(*value, &place)?; } } } @@ -579,7 +578,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut res = 0u64; for i in 0..op_len { - let op = this.read_immediate(&this.project_index(&op, i.into())?.into())?; + let op = this.read_immediate(&this.project_index(&op, i.into())?)?; if simd_element_to_bool(op)? { res |= 1u64 .checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian)) @@ -589,7 +588,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to force the place type to be an int so that we can write `res` into it. let mut dest = this.force_allocation(dest)?; dest.layout = this.machine.layouts.uint(dest.layout.size).unwrap(); - this.write_int(res, &dest.into())?; + this.write_int(res, &dest)?; } name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"), diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index c761c45a5a1c..6667c4d751ff 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -158,7 +158,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { })?; this.write_scalar( Scalar::from_i64(qpc), - &this.deref_operand(lpPerformanceCount_op)?.into(), + &this.deref_operand(lpPerformanceCount_op)?, )?; Ok(Scalar::from_i32(-1)) // return non-zero on success } @@ -179,7 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // and thus 10^9 counts per second. this.write_scalar( Scalar::from_i64(1_000_000_000), - &this.deref_operand_as(lpFrequency_op, this.machine.layouts.u64)?.into(), + &this.deref_operand_as(lpFrequency_op, this.machine.layouts.u64)?, )?; Ok(Scalar::from_i32(-1)) // Return non-zero on success } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 14297845d3db..5f3c15c58749 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -201,14 +201,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_int(einval, dest)?; } else { if size == 0 { - this.write_null(&ret.into())?; + this.write_null(&ret)?; } else { let ptr = this.allocate_ptr( Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), )?; - this.write_pointer(ptr, &ret.into())?; + this.write_pointer(ptr, &ret)?; } this.write_null(dest)?; } @@ -293,7 +293,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Create key and write it into the memory where `key_ptr` wants it. let key = this.machine.tls.create_tls_key(dtor, key_layout.size)?; - this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place.into())?; + this.write_scalar(Scalar::from_uint(key, key_layout.size), &key_place)?; // Return success (`0`). this.write_null(dest)?; @@ -508,7 +508,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [_attr, guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let guard_size = this.deref_operand(guard_size)?; let guard_size_layout = this.libc_ty_layout("size_t"); - this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size.into())?; + this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size)?; // Return success (`0`). this.write_null(dest)?; @@ -538,11 +538,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar( Scalar::from_uint(this.machine.stack_addr, this.pointer_size()), - &addr_place.into(), + &addr_place, )?; this.write_scalar( Scalar::from_uint(this.machine.stack_size, this.pointer_size()), - &size_place.into(), + &size_place, )?; // Return success (`0`). @@ -587,20 +587,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reset all fields to `uninit` to make sure nobody reads them. // (This is a std-only shim so we are okay with such hacks.) - this.write_uninit(&pwd.into())?; + this.write_uninit(&pwd)?; // We only set the home_dir field. #[allow(deprecated)] let home_dir = std::env::home_dir().unwrap(); let (written, _) = this.write_path_to_c_str(&home_dir, buf, buflen)?; let pw_dir = this.project_field_named(&pwd, "pw_dir")?; - this.write_pointer(buf, &pw_dir.into())?; + this.write_pointer(buf, &pw_dir)?; if written { - this.write_pointer(pwd.ptr, &result.into())?; + this.write_pointer(pwd.ptr, &result)?; this.write_null(dest)?; } else { - this.write_null(&result.into())?; + this.write_null(&result)?; this.write_scalar(this.eval_libc("ERANGE"), dest)?; } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 3da6c17f3b0b..fe5b01e76109 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1457,13 +1457,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { )?; let result_place = this.deref_operand(result_op)?; - this.write_scalar(this.read_scalar(entry_op)?, &result_place.into())?; + this.write_scalar(this.read_scalar(entry_op)?, &result_place)?; 0 } None => { // end of stream: return 0, assign *result=NULL - this.write_null(&this.deref_operand(result_op)?.into())?; + this.write_null(&this.deref_operand(result_op)?)?; 0 } Some(Err(e)) => diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs index 9c43651132b7..92966319f19c 100644 --- a/src/tools/miri/src/shims/unix/linux/fd.rs +++ b/src/tools/miri/src/shims/unix/linux/fd.rs @@ -74,9 +74,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let event = this.deref_operand_as(event, this.libc_ty_layout("epoll_event"))?; let events = this.project_field(&event, 0)?; - let events = this.read_scalar(&events.into())?.to_u32()?; + let events = this.read_scalar(&events)?.to_u32()?; let data = this.project_field(&event, 1)?; - let data = this.read_scalar(&data.into())?; + let data = this.read_scalar(&data)?; let event = EpollEvent { events, data }; if let Some(epfd) = this.machine.file_handler.handles.get_mut(&epfd) { @@ -248,8 +248,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let sv1 = fh.insert_fd(Box::new(SocketPair)); let sv1 = ScalarInt::try_from_int(sv1, sv.layout.size).unwrap(); - this.write_scalar(sv0, &sv.into())?; - this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?.into())?; + this.write_scalar(sv0, &sv)?; + this.write_scalar(sv1, &sv.offset(sv.layout.size, sv.layout, this)?)?; Ok(Scalar::from_i32(0)) } diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 85b950da4fe6..3673ca5aee36 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -86,7 +86,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "_NSGetEnviron" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.write_pointer( - this.machine.env_vars.environ.expect("machine must be initialized").ptr, + this.machine.env_vars.environ.as_ref().expect("machine must be initialized").ptr, dest, )?; } @@ -133,7 +133,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let (written, size_needed) = this.write_path_to_c_str( &path, buf_ptr, - this.read_scalar(&bufsize.into())?.to_u32()?.into(), + this.read_scalar(&bufsize)?.to_u32()?.into(), )?; if written { @@ -141,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } else { this.write_scalar( Scalar::from_u32(size_needed.try_into().unwrap()), - &bufsize.into(), + &bufsize, )?; this.write_int(-1, dest)?; } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 1fa0ffd8ee76..428581801d95 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -346,7 +346,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This can always be revisited to have some external state to catch double-destroys // but not complain about the above code. See https://github.com/rust-lang/miri/pull/1933 this.write_uninit( - &this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?.into(), + &this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?, )?; Ok(0) @@ -500,7 +500,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( - &this.deref_operand_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?.into(), + &this.deref_operand_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?, )?; // FIXME: delete interpreter state associated with this mutex. @@ -625,7 +625,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( - &this.deref_operand_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?.into(), + &this.deref_operand_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?, )?; // FIXME: delete interpreter state associated with this rwlock. @@ -675,7 +675,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let clock_id = condattr_get_clock_id(this, attr_op)?; - this.write_scalar(Scalar::from_i32(clock_id), &this.deref_operand(clk_id_op)?.into())?; + this.write_scalar(Scalar::from_i32(clock_id), &this.deref_operand(clk_id_op)?)?; Ok(Scalar::from_i32(0)) } @@ -691,7 +691,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( - &this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?.into(), + &this.deref_operand_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?, )?; Ok(0) @@ -868,7 +868,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( - &this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?.into(), + &this.deref_operand_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?, )?; // FIXME: delete interpreter state associated with this condvar. diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index d64aa53ed958..2f95cfb8bab1 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -125,7 +125,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.project_field_named(&io_status_block, "Information")?; this.write_scalar( Scalar::from_target_usize(n.into(), this), - &io_status_information.into(), + &io_status_information, )?; } // Return whether this was a success. >= 0 is success. diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 172312e331a0..5d5cf73797f3 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -323,7 +323,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let layout = this.machine.layouts.uint(size).unwrap(); let futex_val = this .read_scalar_atomic(&MPlaceTy::from_aligned_ptr(ptr, layout), AtomicReadOrd::Relaxed)?; - let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout).into())?; + let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout))?; if futex_val == compare_val { // If the values are the same, we have to block. diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 92d2425cd3b9..aa75b477473d 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -152,18 +152,13 @@ fn default_handler( TerminalUrl::No, )) }; - Handler::with_emitter( - true, - None, - Box::new(SilentOnIgnoredFilesEmitter { - has_non_ignorable_parser_errors: false, - source_map, - emitter, - ignore_path_set, - can_reset, - }), - None, - ) + Handler::with_emitter(Box::new(SilentOnIgnoredFilesEmitter { + has_non_ignorable_parser_errors: false, + source_map, + emitter, + ignore_path_set, + can_reset, + })) } impl ParseSess { @@ -234,7 +229,7 @@ impl ParseSess { } pub(crate) fn set_silent_emitter(&mut self) { - self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter(), None); + self.parse_sess.span_diagnostic = Handler::with_emitter(silent_emitter()); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index cfad4a8ed0e3..364aa225f680 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -838,6 +838,12 @@ fn handle_result( // Ignore LF and CRLF difference for Windows. if !string_eq_ignore_newline_repr(&fmt_text, &text) { + if let Some(bless) = std::env::var_os("BLESS") { + if bless != "0" { + std::fs::write(file_name, fmt_text).unwrap(); + continue; + } + } let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE); assert!( !diff.is_empty(), diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index bfb967213e13..57cbfe68be46 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -46,6 +46,7 @@ const EXCEPTIONS: &[(&str, &str)] = &[ ("instant", "BSD-3-Clause"), // rustc_driver/tracing-subscriber/parking_lot ("mdbook", "MPL-2.0"), // mdbook ("openssl", "Apache-2.0"), // opt-dist + ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"), // rustc (license is the same as LLVM uses) ("ryu", "Apache-2.0 OR BSL-1.0"), // cargo/... (because of serde) ("self_cell", "Apache-2.0"), // rustc (fluent translations) ("snap", "BSD-3-Clause"), // rustc @@ -224,6 +225,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rustc-hash", "rustc-rayon", "rustc-rayon-core", + "rustc_apfloat", "rustc_version", "rustix", "ruzstd", // via object in thorin-dwp diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index d0257d716973..11480e2be603 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -302,10 +302,6 @@ pub fn check(path: &Path, bad: &mut bool) { return; } } - // apfloat shouldn't be changed because of license problems - if is_in(file, "compiler", "rustc_apfloat") { - return; - } let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr"); let mut skip_undocumented_unsafe = contains_ignore_directive(can_contain, &contents, "undocumented-unsafe"); diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs index 0f79adab7bd6..d24e416b67e9 100644 --- a/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-trait-objects.rs @@ -53,6 +53,18 @@ impl<'a, T, U> Trait4<'a, U> for T { } } +pub trait Trait5 { + fn quux(&self, _: &[T; N]); +} + +#[derive(Copy, Clone)] +pub struct Type5; + +impl Trait5 for T { + fn quux(&self, _: &[U; N]) { + } +} + pub fn foo1(a: &dyn Trait1) { a.foo(); // CHECK-LABEL: define{{.*}}4foo1{{.*}}!type !{{[0-9]+}} @@ -114,7 +126,24 @@ pub fn bar4<'a>() { // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE4:[[:print:]]+]]") } +pub fn foo5(a: &dyn Trait5) { + let b = &[Type5; 32]; + a.quux(&b); + // CHECK-LABEL: define{{.*}}4foo5{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE5:[[:print:]]+]]") +} + +pub fn bar5() { + let a = &[Type5; 32]; + foo5(&a); + let b = &a as &dyn Trait5; + b.quux(&a); + // CHECK-LABEL: define{{.*}}4bar5{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%[0-9]}}, metadata !"[[TYPE5:[[:print:]]+]]") +} + // CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"} // CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE2]]"} // CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE3]]"} // CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE4]]"} +// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE5]]"} diff --git a/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs index 004a67e7df2e..78ecc187b8e5 100644 --- a/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer-kcfi-emit-type-metadata-trait-objects.rs @@ -77,6 +77,19 @@ impl<'a, T, U> Trait4<'a, U> for T { } } +pub trait Trait5 { + fn quux(&self, _: &[T; N]); +} + +pub struct Type5; + +impl Copy for Type5 {} + +impl Trait5 for T { + fn quux(&self, _: &[U; N]) { + } +} + pub fn foo1(a: &dyn Trait1) { a.foo(); // CHECK-LABEL: define{{.*}}4foo1{{.*}}!{{|kcfi_type}} !{{[0-9]+}} @@ -138,7 +151,24 @@ pub fn bar4<'a>() { // CHECK: call align 4 {{ptr|i32\*}} %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z]\.0|%_[0-9]}}, {{\{\}\*|ptr|%Type4\*}} align 1 {{%[a-z]\.0|%_[0-9]}}){{.*}}[ "kcfi"(i32 [[TYPE4:[[:print:]]+]]) ] } +pub fn foo5(a: &dyn Trait5) { + let b = &[Type5; 32]; + a.quux(&b); + // CHECK-LABEL: define{{.*}}4foo5{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, {{\{\}\*|ptr|\[32 x %Type5\]\*}} align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ] +} + +pub fn bar5() { + let a = &[Type5; 32]; + foo5(&a); + let b = &a as &dyn Trait5; + b.quux(&a); + // CHECK-LABEL: define{{.*}}4bar5{{.*}}!{{|kcfi_type}} !{{[0-9]+}} + // CHECK: call void %{{[0-9]}}({{\{\}\*|ptr}} align 1 {{%[a-z](\.0)*|%_[0-9]+]}}, {{\{\}\*|ptr|\[32 x %Type5\]\*}} align 1 {{%[a-z](\.0)*|%_[0-9]+}}){{.*}}[ "kcfi"(i32 [[TYPE5:[[:print:]]+]]) ] +} + // CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]} // CHECK: !{{[0-9]+}} = !{i32 [[TYPE2]]} // CHECK: !{{[0-9]+}} = !{i32 [[TYPE3]]} // CHECK: !{{[0-9]+}} = !{i32 [[TYPE4]]} +// CHECK: !{{[0-9]+}} = !{i32 [[TYPE5]]} diff --git a/tests/rustdoc/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs b/tests/rustdoc/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs new file mode 100644 index 000000000000..a1a716f5a41b --- /dev/null +++ b/tests/rustdoc/auxiliary/issue-113982-doc_auto_cfg-reexport-foreign.rs @@ -0,0 +1,3 @@ +#![crate_name = "colors"] + +pub struct Color; diff --git a/tests/rustdoc/issue-113982-doc_auto_cfg-reexport-foreign.rs b/tests/rustdoc/issue-113982-doc_auto_cfg-reexport-foreign.rs new file mode 100644 index 000000000000..88b86d15c56a --- /dev/null +++ b/tests/rustdoc/issue-113982-doc_auto_cfg-reexport-foreign.rs @@ -0,0 +1,19 @@ +// aux-build: issue-113982-doc_auto_cfg-reexport-foreign.rs + +#![feature(no_core, doc_auto_cfg)] +#![no_core] +#![crate_name = "foo"] + +extern crate colors; + +// @has 'foo/index.html' '//*[@class="stab portability"]' 'Non-colors' +// @has 'foo/struct.Color.html' '//*[@class="stab portability"]' \ +// 'Available on non-crate feature colors only.' +#[cfg(not(feature = "colors"))] +pub use colors::*; + +// @has 'foo/index.html' '//*[@class="stab portability"]' 'Non-fruits' +// @has 'foo/struct.Red.html' '//*[@class="stab portability"]' \ +// 'Available on non-crate feature fruits only.' +#[cfg(not(feature = "fruits"))] +pub use colors::Color as Red; diff --git a/tests/ui/const_prop/apfloat-f64-roundtrip.rs b/tests/ui/const_prop/apfloat-f64-roundtrip.rs new file mode 100644 index 000000000000..9fb2ac96bebd --- /dev/null +++ b/tests/ui/const_prop/apfloat-f64-roundtrip.rs @@ -0,0 +1,9 @@ +// run-pass +// compile-flags: -O -Zmir-opt-level=3 -Cno-prepopulate-passes +// min-llvm-version: 16.0 (requires APFloat fixes in LLVM) + +// Regression test for a broken MIR optimization (issue #113407). +pub fn main() { + let f = f64::from_bits(0x19873cc2) as f32; + assert_eq!(f.to_bits(), 0); +} diff --git a/tests/ui/const_prop/apfloat-remainder-regression.rs b/tests/ui/const_prop/apfloat-remainder-regression.rs new file mode 100644 index 000000000000..08932c333fd8 --- /dev/null +++ b/tests/ui/const_prop/apfloat-remainder-regression.rs @@ -0,0 +1,15 @@ +// run-pass +// compile-flags: -O -Zmir-opt-level=3 -Cno-prepopulate-passes + +// Regression test for a broken MIR optimization (issue #102403). +pub fn f() -> f64 { + std::hint::black_box(-1.0) % std::hint::black_box(-1.0) +} + +pub fn g() -> f64 { + -1.0 % -1.0 +} + +pub fn main() { + assert_eq!(f().signum(), g().signum()); +} diff --git a/tests/ui/lint/lint-ctypes-113436-1.rs b/tests/ui/lint/lint-ctypes-113436-1.rs new file mode 100644 index 000000000000..1ca59c6868d6 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436-1.rs @@ -0,0 +1,28 @@ +#![deny(improper_ctypes_definitions)] + +#[repr(C)] +pub struct Foo { + a: u8, + b: (), +} + +extern "C" fn foo(x: Foo) -> Foo { + todo!() +} + +struct NotSafe(u32); + +#[repr(C)] +pub struct Bar { + a: u8, + b: (), + c: NotSafe, +} + +extern "C" fn bar(x: Bar) -> Bar { + //~^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe + //~^^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe + todo!() +} + +fn main() {} diff --git a/tests/ui/lint/lint-ctypes-113436-1.stderr b/tests/ui/lint/lint-ctypes-113436-1.stderr new file mode 100644 index 000000000000..7b63043f0575 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436-1.stderr @@ -0,0 +1,35 @@ +error: `extern` fn uses type `NotSafe`, which is not FFI-safe + --> $DIR/lint-ctypes-113436-1.rs:22:22 + | +LL | extern "C" fn bar(x: Bar) -> Bar { + | ^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-ctypes-113436-1.rs:1:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `NotSafe`, which is not FFI-safe + --> $DIR/lint-ctypes-113436-1.rs:22:30 + | +LL | extern "C" fn bar(x: Bar) -> Bar { + | ^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/lint-ctypes-113436.rs new file mode 100644 index 000000000000..4f733b5bb16f --- /dev/null +++ b/tests/ui/lint/lint-ctypes-113436.rs @@ -0,0 +1,34 @@ +// check-pass +#![deny(improper_ctypes_definitions)] + +#[repr(C)] +pub struct Wrap(T); + +#[repr(transparent)] +pub struct TransparentWrap(T); + +pub extern "C" fn f() -> Wrap<()> { + todo!() +} + +const _: extern "C" fn() -> Wrap<()> = f; + +pub extern "C" fn ff() -> Wrap> { + todo!() +} + +const _: extern "C" fn() -> Wrap> = ff; + +pub extern "C" fn g() -> TransparentWrap<()> { + todo!() +} + +const _: extern "C" fn() -> TransparentWrap<()> = g; + +pub extern "C" fn gg() -> TransparentWrap> { + todo!() +} + +const _: extern "C" fn() -> TransparentWrap> = gg; + +fn main() {} diff --git a/tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs b/tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs new file mode 100644 index 000000000000..64ff1f8b1d2d --- /dev/null +++ b/tests/ui/numbers-arithmetic/apfloat-modulo-wrong.rs @@ -0,0 +1,15 @@ +// run-pass +// check-run-results +// regression test for issue #109567 + +fn f() -> f64 { + std::hint::black_box(-1.0) % std::hint::black_box(-1.0) +} + +const G: f64 = -1.0 % -1.0; + +pub fn main() { + assert_eq!(-1, G.signum() as i32); + assert_eq!((-0.0_f64).to_bits(), G.to_bits()); + assert_eq!(f().signum(), G.signum()); +} diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index 17b69daa4f0e..4a2200091b27 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -18,7 +18,7 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#1 */; -extern crate compiler_builtins /* 442 */ as _ /* 0#1 */; +extern crate compiler_builtins /* 443 */ as _ /* 0#1 */; // Don't load unnecessary hygiene information from std extern crate std /* 0#0 */; diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index 76d54ab2f138..077a728a7a6b 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#2 */; -extern crate compiler_builtins /* 442 */ as _ /* 0#2 */; +extern crate compiler_builtins /* 443 */ as _ /* 0#2 */; // Don't load unnecessary hygiene information from std extern crate std /* 0#0 */; diff --git a/triagebot.toml b/triagebot.toml index cc26c3b3e7b9..c947c3f19301 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -318,14 +318,6 @@ changelog-branch = "master" [shortcut] - -[mentions."compiler/rustc_apfloat"] -message = """ -Changes rustc_apfloat. rustc_apfloat is currently in limbo and you almost \ -certainly don't want to change it (see #55993). -""" -cc = ["@eddyb"] - [mentions."compiler/rustc_codegen_cranelift"] cc = ["@bjorn3"] @@ -609,7 +601,6 @@ style-team = [ "/Cargo.lock" = ["@Mark-Simulacrum"] "/Cargo.toml" = ["@Mark-Simulacrum"] "/compiler" = ["compiler"] -"/compiler/rustc_apfloat" = ["@eddyb"] "/compiler/rustc_ast" = ["compiler", "parser"] "/compiler/rustc_ast_lowering" = ["compiler", "ast_lowering"] "/compiler/rustc_hir_analysis" = ["compiler", "types"]