diff options
Diffstat (limited to 'compiler')
566 files changed, 10723 insertions, 15985 deletions
diff --git a/compiler/rustc/build.rs b/compiler/rustc/build.rs index 39cf3e094c8..8b7d28d2b8a 100644 --- a/compiler/rustc/build.rs +++ b/compiler/rustc/build.rs @@ -18,7 +18,7 @@ fn set_windows_exe_options() { let mut manifest = env::current_dir().unwrap(); manifest.push(WINDOWS_MANIFEST_FILE); - println!("cargo:rerun-if-changed={}", WINDOWS_MANIFEST_FILE); + println!("cargo:rerun-if-changed={WINDOWS_MANIFEST_FILE}"); // Embed the Windows application manifest file. println!("cargo:rustc-link-arg-bin=rustc-main=/MANIFEST:EMBED"); println!("cargo:rustc-link-arg-bin=rustc-main=/MANIFESTINPUT:{}", manifest.to_str().unwrap()); diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index aea88641f82..a8a1a90572d 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -260,8 +260,7 @@ pub trait LayoutCalculator { } _ => assert!( start == Bound::Unbounded && end == Bound::Unbounded, - "nonscalar layout for layout_scalar_valid_range type: {:#?}", - st, + "nonscalar layout for layout_scalar_valid_range type: {st:#?}", ), } @@ -463,7 +462,7 @@ pub trait LayoutCalculator { min = 0; max = 0; } - assert!(min <= max, "discriminant range is {}...{}", min, max); + assert!(min <= max, "discriminant range is {min}...{max}"); let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max); let mut align = dl.aggregate_align; @@ -537,8 +536,7 @@ pub trait LayoutCalculator { // space necessary to represent would have to be discarded (or layout is wrong // on thinking it needs 16 bits) panic!( - "layout decided on a larger discriminant type ({:?}) than typeck ({:?})", - min_ity, typeck_ity + "layout decided on a larger discriminant type ({min_ity:?}) than typeck ({typeck_ity:?})" ); // However, it is fine to make discr type however large (as an optimisation) // after this point – we’ll just truncate the value we load in codegen. @@ -763,7 +761,9 @@ pub trait LayoutCalculator { let mut size = Size::ZERO; let only_variant = &variants[FIRST_VARIANT]; for field in only_variant { - assert!(field.0.is_sized()); + if field.0.is_unsized() { + self.delay_bug("unsized field in union".to_string()); + } align = align.max(field.align()); max_repr_align = max_repr_align.max(field.max_repr_align()); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index ef0c763ac20..1442747fe1e 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -332,7 +332,7 @@ impl TargetDataLayout { 16 => 1 << 15, 32 => 1 << 31, 64 => 1 << 47, - bits => panic!("obj_size_bound: unknown pointer bit size {}", bits), + bits => panic!("obj_size_bound: unknown pointer bit size {bits}"), } } @@ -342,7 +342,7 @@ impl TargetDataLayout { 16 => I16, 32 => I32, 64 => I64, - bits => panic!("ptr_sized_integer: unknown pointer bit size {}", bits), + bits => panic!("ptr_sized_integer: unknown pointer bit size {bits}"), } } @@ -399,7 +399,7 @@ impl FromStr for Endian { match s { "little" => Ok(Self::Little), "big" => Ok(Self::Big), - _ => Err(format!(r#"unknown endian: "{}""#, s)), + _ => Err(format!(r#"unknown endian: "{s}""#)), } } } @@ -456,7 +456,7 @@ impl Size { pub fn bits(self) -> u64 { #[cold] fn overflow(bytes: u64) -> ! { - panic!("Size::bits: {} bytes in bits doesn't fit in u64", bytes) + panic!("Size::bits: {bytes} bytes in bits doesn't fit in u64") } self.bytes().checked_mul(8).unwrap_or_else(|| overflow(self.bytes())) @@ -1179,17 +1179,12 @@ impl FieldsShape { unreachable!("FieldsShape::offset: `Primitive`s have no fields") } FieldsShape::Union(count) => { - assert!( - i < count.get(), - "tried to access field {} of union with {} fields", - i, - count - ); + assert!(i < count.get(), "tried to access field {i} of union with {count} fields"); Size::ZERO } FieldsShape::Array { stride, count } => { let i = u64::try_from(i).unwrap(); - assert!(i < count); + assert!(i < count, "tried to access field {i} of array with {count} fields"); stride * i } FieldsShape::Arbitrary { ref offsets, .. } => offsets[FieldIdx::from_usize(i)], @@ -1294,7 +1289,7 @@ impl Abi { Primitive::Int(_, signed) => signed, _ => false, }, - _ => panic!("`is_signed` on non-scalar ABI {:?}", self), + _ => panic!("`is_signed` on non-scalar ABI {self:?}"), } } @@ -1345,7 +1340,6 @@ impl Abi { /// Discard validity range information and allow undef. pub fn to_union(&self) -> Self { - assert!(self.is_sized()); match *self { Abi::Scalar(s) => Abi::Scalar(s.to_union()), Abi::ScalarPair(s1, s2) => Abi::ScalarPair(s1.to_union(), s2.to_union()), diff --git a/compiler/rustc_apfloat/Cargo.toml b/compiler/rustc_apfloat/Cargo.toml deleted file mode 100644 index 98305201bc9..00000000000 --- 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 2286712f025..00000000000 --- 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<S> { - /// 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<S>, -} - -/// 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 2<sup>E</sup> is representable; this matches the - /// definition of IEEE 754. - const MAX_EXP: ExpInt; - - /// The smallest E such that 2<sup>E</sup> 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<Self> { - 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<Self>) -> 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<S> Copy for IeeeFloat<S> {} -impl<S> Clone for IeeeFloat<S> { - 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<X87DoubleExtendedS>; -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<Self> { - 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<Self>) -> 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<S>); - -impl<S: Semantics> PartialEq for IeeeFloat<S> { - fn eq(&self, rhs: &Self) -> bool { - self.partial_cmp(rhs) == Some(Ordering::Equal) - } -} - -impl<S: Semantics> PartialOrd for IeeeFloat<S> { - fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { - 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<S> Neg for IeeeFloat<S> { - 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<S: Semantics> fmt::Display for IeeeFloat<S> { - 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<S: Semantics> fmt::Debug for IeeeFloat<S> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}({:?} | {}{:?} * 2^{})", - self, - self.category, - if self.sign { "-" } else { "+" }, - self.sig, - self.exp - ) - } -} - -impl<S: Semantics> Float for IeeeFloat<S> { - 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<u128>) -> 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<u128>) -> 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<Self> { - 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> { - 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<Self> { - // 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> { - 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<Self> { - 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<Self> { - // 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<Self> { - // 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<Self> { - 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<StatusAnd<Self>, 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<u128> { - // 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<Self> { - // 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<S: Semantics, T: Semantics> FloatConvert<IeeeFloat<T>> for IeeeFloat<S> { - fn convert_r(self, round: Round, loses_info: &mut bool) -> StatusAnd<IeeeFloat<T>> { - 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<S: Semantics>() -> bool { - S::QNAN_SIGNIFICAND == X87DoubleExtendedS::QNAN_SIGNIFICAND - } - let x87_special_nan = is_x87_double_extended::<S>() - && !is_x87_double_extended::<T>() - && 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::<T>() { - 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<S: Semantics> IeeeFloat<S> { - /// 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<Self> { - 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<Self> { - 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<StatusAnd<Self>, 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<StatusAnd<Self>, 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<ExpInt> { - 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<F: FnMut(Limb) -> 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 n<sup>2</sup>. - 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 dde368e7b92..00000000000 --- 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): -//! <https://github.com/llvm-mirror/llvm/tree/23efab2bbd424ed13495a420ad8641cb2c6c28f9> -//! -//! * `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<T> { - pub status: Status, - pub value: T, -} - -impl Status { - pub fn and<T>(self, value: T) -> StatusAnd<T> { - StatusAnd { status: self, value } - } -} - -impl<T> StatusAnd<T> { - pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> StatusAnd<U> { - 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<Err = ParseError> - + PartialOrd - + fmt::Display - + Neg<Output = Self> - + AddAssign - + SubAssign - + MulAssign - + DivAssign - + RemAssign - + Add<Output = StatusAnd<Self>> - + Sub<Output = StatusAnd<Self>> - + Mul<Output = StatusAnd<Self>> - + Div<Output = StatusAnd<Self>> - + Rem<Output = StatusAnd<Self>> -{ - /// 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 2<sup>E</sup> is representable; this matches the - /// definition of IEEE 754. - const MAX_EXP: ExpInt; - - /// The smallest E such that 2<sup>E</sup> 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<u128>) -> Self; - - /// Factory for SNaN values. - // FIXME(eddyb) should be const fn. - fn snan(payload: Option<u128>) -> 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<Self>; - fn sub_r(self, rhs: Self, round: Round) -> StatusAnd<Self> { - self.add_r(-rhs, round) - } - fn mul_r(self, rhs: Self, round: Round) -> StatusAnd<Self>; - fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self>; - fn mul_add(self, multiplicand: Self, addend: Self) -> StatusAnd<Self> { - self.mul_add_r(multiplicand, addend, Round::NearestTiesToEven) - } - fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self>; - /// IEEE remainder. - // This is not currently correct in all cases. - fn ieee_rem(self, rhs: Self) -> StatusAnd<Self> { - 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<Self>; - fn round_to_integral(self, round: Round) -> StatusAnd<Self>; - - /// IEEE-754R 2008 5.3.1: nextUp. - fn next_up(self) -> StatusAnd<Self>; - - /// 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> { - (-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<Self> { - 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> { - Self::from_i128_r(input, Round::NearestTiesToEven) - } - fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self>; - fn from_u128(input: u128) -> StatusAnd<Self> { - Self::from_u128_r(input, Round::NearestTiesToEven) - } - fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, 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<i128> { - 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<i128> { - self.to_i128_r(width, Round::TowardZero, &mut true) - } - fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128>; - fn to_u128(self, width: usize) -> StatusAnd<u128> { - 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<Self>; - - /// 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 * 2<sup>exp</sup> 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<T: Float>: 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<T>; - fn convert(self, loses_info: &mut bool) -> StatusAnd<T> { - 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, ParseError> { - 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<Self>; - fn add(self, rhs: Self) -> StatusAnd<Self> { - self.add_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Sub for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd<Self>; - fn sub(self, rhs: Self) -> StatusAnd<Self> { - self.sub_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Mul for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd<Self>; - fn mul(self, rhs: Self) -> StatusAnd<Self> { - self.mul_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Div for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd<Self>; - fn div(self, rhs: Self) -> StatusAnd<Self> { - self.div_r(rhs, Round::NearestTiesToEven) - } - } - - impl<$t> ::core::ops::Rem for $ty<$t> - where - Self: Float, - { - type Output = StatusAnd<Self>; - fn rem(self, rhs: Self) -> StatusAnd<Self> { - 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 65a0f66645b..00000000000 --- 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, F); -pub type DoubleDouble = DoubleFloat<ieee::Double>; - -// 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<F>(#[allow(unused)] F); -type Fallback<F> = ieee::IeeeFloat<FallbackS<F>>; -impl<F: Float> ieee::Semantics for FallbackS<F> { - // 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<F>(#[allow(unused)] F); -type FallbackExtended<F> = ieee::IeeeFloat<FallbackExtendedS<F>>; -impl<F: Float> ieee::Semantics for FallbackExtendedS<F> { - // Forbid any conversion to/from bits. - const BITS: usize = 0; - const PRECISION: usize = Fallback::<F>::PRECISION; - const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt; -} - -impl<F: Float> From<Fallback<F>> for DoubleFloat<F> -where - F: FloatConvert<FallbackExtended<F>>, - FallbackExtended<F>: FloatConvert<F>, -{ - fn from(x: Fallback<F>) -> Self { - let mut status; - let mut loses_info = false; - - let extended: FallbackExtended<F> = 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<F> = 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<F: FloatConvert<Self>> From<DoubleFloat<F>> for Fallback<F> { - fn from(DoubleFloat(a, b): DoubleFloat<F>) -> 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<F>); - -impl<F: Float> Neg for DoubleFloat<F> { - 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<F: FloatConvert<Fallback<F>>> fmt::Display for DoubleFloat<F> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&Fallback::from(*self), f) - } -} - -impl<F: FloatConvert<Fallback<F>>> Float for DoubleFloat<F> -where - Self: From<Fallback<F>>, -{ - const BITS: usize = F::BITS * 2; - const PRECISION: usize = Fallback::<F>::PRECISION; - const MAX_EXP: ExpInt = Fallback::<F>::MAX_EXP; - const MIN_EXP: ExpInt = Fallback::<F>::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<u128>) -> Self { - DoubleFloat(F::qnan(payload), F::ZERO) - } - - fn snan(payload: Option<u128>) -> 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<Self> { - 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<Self> { - // 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<Self> { - 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<Self> { - Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from) - } - - fn c_fmod(self, rhs: Self) -> StatusAnd<Self> { - Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from) - } - - fn round_to_integral(self, round: Round) -> StatusAnd<Self> { - Fallback::from(self).round_to_integral(round).map(Self::from) - } - - fn next_up(self) -> StatusAnd<Self> { - 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<Self> { - Fallback::from_u128_r(input, round).map(Self::from) - } - - fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, 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<u128> { - 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<Self> { - 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 f8fac0c2358..00000000000 --- 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::<Quad>().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::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x0.0000000000000000000000000002p-16382".parse::<Quad>().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::<Quad>() - .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::<Quad>() - .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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x0.0000000000000000000000000002p-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x1.0000000000000000000000000000p-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x1.0000000000000000000000000000p-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "-0x0.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "+0x0.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().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::<Quad>().unwrap().next_up()); - let expected = "-0x1.ffffffffffffffffffffffffffffp+0".parse::<Quad>().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::<Quad>().unwrap().next_down()); - let expected = "0x1.ffffffffffffffffffffffffffffp+0".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x1p+1".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x1p+1".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "-0x0.fffffffffffffffffffffffffffep-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "0x0.fffffffffffffffffffffffffffep-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x1.0000000000000000000000000001p-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x1.0000000000000000000000000001p-16382".parse::<Quad>().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::<Quad>().unwrap().next_up()); - let expected = "-0x1.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x1p-16381".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x1p-16381".parse::<Quad>().unwrap(); - assert_eq!(status, Status::OK); - assert!(test.bitwise_eq(expected)); - - // nextDown(0x1p-16381) -> 0x1.ffffffffffffffffffffffffffffp-16382 - let test = unpack!(status=, "0x1p-16381".parse::<Quad>().unwrap().next_down()); - let expected = "0x1.ffffffffffffffffffffffffffffp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x0.ffffffffffffffffffffffff000dp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "0x0.ffffffffffffffffffffffff000bp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "-0x0.ffffffffffffffffffffffff000bp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x0.ffffffffffffffffffffffff000dp-16382".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "0x1.ffffffffffffffffffffffff000dp-16000".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "0x1.ffffffffffffffffffffffff000bp-16000".parse::<Quad>().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::<Quad>() - .unwrap() - .next_up()); - let expected = "-0x1.ffffffffffffffffffffffff000bp-16000".parse::<Quad>().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::<Quad>() - .unwrap() - .next_down()); - let expected = "-0x1.ffffffffffffffffffffffff000dp-16000".parse::<Quad>().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::<Double>().unwrap(); - let f2 = "+0x1p-1074".parse::<Double>().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::<Single>().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::<Double>().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::<X87DoubleExtended>().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::<Quad>().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::<Double>().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.01"[..3].parse::<Double>().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.09"[..3].parse::<Double>().unwrap(); - assert_eq!(val.to_f64(), 0.0); - let val = "0.095"[..4].parse::<Double>().unwrap(); - assert_eq!(val.to_f64(), 0.09); - let val = "0.00e+3"[..7].parse::<Double>().unwrap(); - assert_eq!(val.to_f64(), 0.00); - let val = "0e+3"[..4].parse::<Double>().unwrap(); - assert_eq!(val.to_f64(), 0.00); -} - -#[test] -fn from_zero_decimal_string() { - assert_eq!(0.0, "0".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, ".0".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+.0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-.0".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.0".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "00000.".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+00000.".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-00000.".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, ".00000".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+.00000".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-.00000".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0000.00000".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0000.00000".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0000.00000".parse::<Double>().unwrap().to_f64()); -} - -#[test] -fn from_zero_decimal_single_exponent_string() { - assert_eq!(0.0, "0e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0e1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0e+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0e-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.e-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, ".0e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+.0e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, ".0e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+.0e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, ".0e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+.0e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-.0e-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0.0e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0.0e-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0.0e-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "000.0000e1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+000.0000e+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-000.0000e+1".parse::<Double>().unwrap().to_f64()); -} - -#[test] -fn from_zero_decimal_large_exponent_string() { - assert_eq!(0.0, "0e1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0e1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0e1234".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0e+1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0e+1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0e+1234".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0e-1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0e-1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0e-1234".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "000.0000e1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "000.0000e-1234".parse::<Double>().unwrap().to_f64()); -} - -#[test] -fn from_zero_hexadecimal_string() { - assert_eq!(0.0, "0x0p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x.0p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x.0p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x.0p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x0.0p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "+0x0.0p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0.0p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.0, "0x00000.p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x0000.00000p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x.00000p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x0.p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x0p1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.0, "-0x0p1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x00000.p1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x0000.00000p1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x.00000p1234".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.0, "0x0.p1234".parse::<Double>().unwrap().to_f64()); -} - -#[test] -fn from_decimal_string() { - assert_eq!(1.0, "1".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0, "2.".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.5, ".5".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.0, "1.0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-2.0, "-2".parse::<Double>().unwrap().to_f64()); - assert_eq!(-4.0, "-4.".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.5, "-.5".parse::<Double>().unwrap().to_f64()); - assert_eq!(-1.5, "-1.5".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.25e12, "1.25e12".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.25e+12, "1.25e+12".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.25e-12, "1.25e-12".parse::<Double>().unwrap().to_f64()); - assert_eq!(1024.0, "1024.".parse::<Double>().unwrap().to_f64()); - assert_eq!(1024.05, "1024.05000".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.05, ".05000".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0, "2.".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0e2, "2.e2".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0e+2, "2.e+2".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0e-2, "2.e-2".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.05e2, "002.05000e2".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.05e+2, "002.05000e+2".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.05e-2, "002.05000e-2".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.05e12, "002.05000e12".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.05e+12, "002.05000e+12".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.05e-12, "002.05000e-12".parse::<Double>().unwrap().to_f64()); - - // These are "carefully selected" to overflow the fast log-base - // calculations in the implementation. - assert!("99e99999".parse::<Double>().unwrap().is_infinite()); - assert!("-99e99999".parse::<Double>().unwrap().is_infinite()); - assert!("1e-99999".parse::<Double>().unwrap().is_pos_zero()); - assert!("-1e-99999".parse::<Double>().unwrap().is_neg_zero()); - - assert_eq!(2.71828, "2.71828".parse::<Double>().unwrap().to_f64()); -} - -#[test] -fn from_hexadecimal_string() { - assert_eq!(1.0, "0x1p0".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p0".parse::<Double>().unwrap().to_f64()); - - assert_eq!(1.0, "0x1p+0".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p+0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p+0".parse::<Double>().unwrap().to_f64()); - - assert_eq!(1.0, "0x1p-0".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.0, "+0x1p-0".parse::<Double>().unwrap().to_f64()); - assert_eq!(-1.0, "-0x1p-0".parse::<Double>().unwrap().to_f64()); - - assert_eq!(2.0, "0x1p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0, "+0x1p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-2.0, "-0x1p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(2.0, "0x1p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(2.0, "+0x1p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-2.0, "-0x1p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.5, "0x1p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.5, "+0x1p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.5, "-0x1p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(3.0, "0x1.8p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(3.0, "+0x1.8p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-3.0, "-0x1.8p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(3.0, "0x1.8p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(3.0, "+0x1.8p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-3.0, "-0x1.8p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.75, "0x1.8p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.75, "+0x1.8p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.75, "-0x1.8p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000.000p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000.000p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000.000p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000.000p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000.000p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000.000p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(2048.0, "0x1000.000p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(2048.0, "+0x1000.000p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-2048.0, "-0x1000.000p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000p1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000p1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(8192.0, "0x1000p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(8192.0, "+0x1000p+1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-8192.0, "-0x1000p+1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(2048.0, "0x1000p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(2048.0, "+0x1000p-1".parse::<Double>().unwrap().to_f64()); - assert_eq!(-2048.0, "-0x1000p-1".parse::<Double>().unwrap().to_f64()); - - assert_eq!(16384.0, "0x10p10".parse::<Double>().unwrap().to_f64()); - assert_eq!(16384.0, "+0x10p10".parse::<Double>().unwrap().to_f64()); - assert_eq!(-16384.0, "-0x10p10".parse::<Double>().unwrap().to_f64()); - - assert_eq!(16384.0, "0x10p+10".parse::<Double>().unwrap().to_f64()); - assert_eq!(16384.0, "+0x10p+10".parse::<Double>().unwrap().to_f64()); - assert_eq!(-16384.0, "-0x10p+10".parse::<Double>().unwrap().to_f64()); - - assert_eq!(0.015625, "0x10p-10".parse::<Double>().unwrap().to_f64()); - assert_eq!(0.015625, "+0x10p-10".parse::<Double>().unwrap().to_f64()); - assert_eq!(-0.015625, "-0x10p-10".parse::<Double>().unwrap().to_f64()); - - assert_eq!(1.0625, "0x1.1p0".parse::<Double>().unwrap().to_f64()); - assert_eq!(1.0, "0x1p0".parse::<Double>().unwrap().to_f64()); - - assert_eq!( - "0x1p-150".parse::<Double>().unwrap().to_f64(), - "+0x800000000000000001.p-221".parse::<Double>().unwrap().to_f64() - ); - assert_eq!( - 2251799813685248.5, - "0x80000000000004000000.010p-28".parse::<Double>().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::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(is_exact); - - assert_eq!( - Status::INVALID_OP.and(0), - "-10".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INVALID_OP.and(31), - "32".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INEXACT.and(7), - "7.9".parse::<Double>().unwrap().to_u128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::OK.and(-10), - "-10".parse::<Double>().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(is_exact); - - assert_eq!( - Status::INVALID_OP.and(-16), - "-17".parse::<Double>().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); - - assert_eq!( - Status::INVALID_OP.and(15), - "16".parse::<Double>().unwrap().to_i128_r(5, Round::TowardZero, &mut is_exact,) - ); - assert!(!is_exact); -} - -#[test] -fn nan() { - fn nanbits<T: Float>(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::<Single>(false, false, 0)); - assert_eq!(0xffc00000, nanbits::<Single>(false, true, 0)); - assert_eq!(0x7fc0ae72, nanbits::<Single>(false, false, 0xae72)); - assert_eq!(0x7fffae72, nanbits::<Single>(false, false, 0xffffae72)); - assert_eq!(0x7fa00000, nanbits::<Single>(true, false, 0)); - assert_eq!(0xffa00000, nanbits::<Single>(true, true, 0)); - assert_eq!(0x7f80ae72, nanbits::<Single>(true, false, 0xae72)); - assert_eq!(0x7fbfae72, nanbits::<Single>(true, false, 0xffffae72)); - - assert_eq!(0x7ff8000000000000, nanbits::<Double>(false, false, 0)); - assert_eq!(0xfff8000000000000, nanbits::<Double>(false, true, 0)); - assert_eq!(0x7ff800000000ae72, nanbits::<Double>(false, false, 0xae72)); - assert_eq!(0x7fffffffffffae72, nanbits::<Double>(false, false, 0xffffffffffffae72)); - assert_eq!(0x7ff4000000000000, nanbits::<Double>(true, false, 0)); - assert_eq!(0xfff4000000000000, nanbits::<Double>(true, true, 0)); - assert_eq!(0x7ff000000000ae72, nanbits::<Double>(true, false, 0xae72)); - assert_eq!(0x7ff7ffffffffae72, nanbits::<Double>(true, false, 0xffffffffffffae72)); -} - -#[test] -fn string_decimal_death() { - assert_eq!("".parse::<Double>(), Err(ParseError("Invalid string length"))); - assert_eq!("+".parse::<Double>(), Err(ParseError("String has no digits"))); - assert_eq!("-".parse::<Double>(), Err(ParseError("String has no digits"))); - - assert_eq!("\0".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\0".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\02".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1\02e1".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("1e\0".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("1e1\0".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("1e1\02".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("1.0f".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - - assert_eq!("..".parse::<Double>(), Err(ParseError("String contains multiple dots"))); - assert_eq!("..0".parse::<Double>(), Err(ParseError("String contains multiple dots"))); - assert_eq!("1.0.0".parse::<Double>(), Err(ParseError("String contains multiple dots"))); -} - -#[test] -fn string_decimal_significand_death() { - assert_eq!(".".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("e".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+e".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-e".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("e1".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+e1".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-e1".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!(".e1".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.e1".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.e1".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!(".e".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+.e".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-.e".parse::<Double>(), Err(ParseError("Significand has no digits"))); -} - -#[test] -fn string_decimal_exponent_death() { - assert_eq!("1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1.e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1.e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!(".1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+.1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-.1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+1.1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-1.1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1e+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1e-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!(".1e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!(".1e+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!(".1e-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("1.0e".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1.0e+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("1.0e-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); -} - -#[test] -fn string_hexadecimal_death() { - assert_eq!("0x".parse::<Double>(), Err(ParseError("Invalid string"))); - assert_eq!("+0x".parse::<Double>(), Err(ParseError("Invalid string"))); - assert_eq!("-0x".parse::<Double>(), Err(ParseError("Invalid string"))); - - assert_eq!("0x0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x0.".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0.".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0.".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x0.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("+0x0.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - assert_eq!("-0x0.0".parse::<Double>(), Err(ParseError("Hex strings require an exponent"))); - - assert_eq!("0x\0".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\0".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\02".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1\02p1".parse::<Double>(), Err(ParseError("Invalid character in significand"))); - assert_eq!("0x1p\0".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("0x1p1\0".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - assert_eq!("0x1p1\02".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("0x1p0f".parse::<Double>(), Err(ParseError("Invalid character in exponent"))); - - assert_eq!("0x..p1".parse::<Double>(), Err(ParseError("String contains multiple dots"))); - assert_eq!("0x..0p1".parse::<Double>(), Err(ParseError("String contains multiple dots"))); - assert_eq!("0x1.0.0p1".parse::<Double>(), Err(ParseError("String contains multiple dots"))); -} - -#[test] -fn string_hexadecimal_significand_death() { - assert_eq!("0x.".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp+".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp+".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp+".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0xp-".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0xp-".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0xp-".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p+".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p+".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p+".parse::<Double>(), Err(ParseError("Significand has no digits"))); - - assert_eq!("0x.p-".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("+0x.p-".parse::<Double>(), Err(ParseError("Significand has no digits"))); - assert_eq!("-0x.p-".parse::<Double>(), Err(ParseError("Significand has no digits"))); -} - -#[test] -fn string_hexadecimal_exponent_death() { - assert_eq!("0x1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p+".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - - assert_eq!("0x1.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("+0x1.1p-".parse::<Double>(), Err(ParseError("Exponent has no digits"))); - assert_eq!("-0x1.1p-".parse::<Double>(), 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::<Quad>() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::<Quad>().unwrap()) - ); - assert!( - "2.0" - .parse::<X87DoubleExtended>() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::<X87DoubleExtended>().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::<Single>().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::<Single>().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::<Quad>().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::<Quad>().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::<Single>().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::<Single>().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::<Quad>().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::<Quad>().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<T: Float>(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::<T>().unwrap(); - assert!(test.is_zero()); - assert_eq!(sign, test.is_negative()); - assert!(test.bitwise_eq(expected)); - assert_eq!(bits, test.to_bits()); - } - test::<Half>(false, 0); - test::<Half>(true, 0x8000); - test::<Single>(false, 0); - test::<Single>(true, 0x80000000); - test::<Double>(false, 0); - test::<Double>(true, 0x8000000000000000); - test::<Quad>(false, 0); - test::<Quad>(true, 0x8000000000000000_0000000000000000); - test::<X87DoubleExtended>(false, 0); - test::<X87DoubleExtended>(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::<Double>().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::<X87DoubleExtended>().unwrap(); - let one = "1.0".parse::<X87DoubleExtended>().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::<Quad>().unwrap(); - let one = "1.0".parse::<Quad>().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::<X87DoubleExtended>().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::<Single>().unwrap(); - assert!(!t.is_negative()); - let t = "-0x1p+0".parse::<Single>().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::<Single>().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::<Single>().unwrap().is_normal()); -} - -#[test] -fn is_finite() { - let t = "0x1p+0".parse::<Single>().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::<Single>().unwrap().is_finite()); -} - -#[test] -fn is_infinite() { - let t = "0x1p+0".parse::<Single>().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::<Single>().unwrap().is_infinite()); -} - -#[test] -fn is_nan() { - let t = "0x1p+0".parse::<Single>().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::<Single>().unwrap().is_nan()); -} - -#[test] -fn is_finite_non_zero() { - // Test positive/negative normal value. - assert!("0x1p+0".parse::<Single>().unwrap().is_finite_non_zero()); - assert!("-0x1p+0".parse::<Single>().unwrap().is_finite_non_zero()); - - // Test positive/negative denormal value. - assert!("0x1p-149".parse::<Single>().unwrap().is_finite_non_zero()); - assert!("-0x1p-149".parse::<Single>().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::<Single>().unwrap(); - let m_normal_value = "-0x1p+0".parse::<Single>().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::<Single>().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::<Single>().unwrap(); - let m_normal_value = "-0x1p+0".parse::<Single>().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::<Single>().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::<Single>().unwrap(); - let m_normal_value = "-0x1p+0".parse::<Single>().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::<Single>().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::<Single>().unwrap(); - let m_normal_value = "-0x1p+0".parse::<Single>().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::<Single>().unwrap())); - } -} - -#[test] -fn operator_overloads() { - // This is mostly testing that these operator overloads compile. - let one = "0x1p+0".parse::<Single>().unwrap(); - let two = "0x2p+0".parse::<Single>().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::<Single>().unwrap(); - let m_normal_value = "-0x1p+0".parse::<Single>().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::<Single>().unwrap(); - let neg_one = "-1.0".parse::<Single>().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::<Double>().unwrap().ilogb()); - assert_eq!(-1023, "0x1.ffffffffffffep-1023".parse::<Double>().unwrap().ilogb()); - assert_eq!(-1023, "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap().ilogb()); - assert_eq!(-51, "0x1p-51".parse::<Double>().unwrap().ilogb()); - assert_eq!(-1023, "0x1.c60f120d9f87cp-1023".parse::<Double>().unwrap().ilogb()); - assert_eq!(-2, "0x0.ffffp-1".parse::<Double>().unwrap().ilogb()); - assert_eq!(-1023, "0x1.fffep-1023".parse::<Double>().unwrap().ilogb()); - assert_eq!(1023, Double::largest().ilogb()); - assert_eq!(1023, (-Double::largest()).ilogb()); - - assert_eq!(0, "0x1p+0".parse::<Single>().unwrap().ilogb()); - assert_eq!(0, "-0x1p+0".parse::<Single>().unwrap().ilogb()); - assert_eq!(42, "0x1p+42".parse::<Single>().unwrap().ilogb()); - assert_eq!(-42, "0x1p-42".parse::<Single>().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::<Single>() - .unwrap() - .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(0),) - ); - assert!( - "0x1p+42" - .parse::<Single>() - .unwrap() - .bitwise_eq("0x1p+0".parse::<Single>().unwrap().scalbn(42),) - ); - assert!( - "0x1p-42" - .parse::<Single>() - .unwrap() - .bitwise_eq("0x1p+0".parse::<Single>().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::<Single>().unwrap().scalbn(128),)); - assert!(m_inf.bitwise_eq("-0x1p+0".parse::<Single>().unwrap().scalbn(128),)); - assert!(p_inf.bitwise_eq("0x1p+127".parse::<Single>().unwrap().scalbn(1),)); - assert!(p_zero.bitwise_eq("0x1p-127".parse::<Single>().unwrap().scalbn(-127),)); - assert!(m_zero.bitwise_eq("-0x1p-127".parse::<Single>().unwrap().scalbn(-127),)); - assert!( - "-0x1p-149" - .parse::<Single>() - .unwrap() - .bitwise_eq("-0x1p-127".parse::<Single>().unwrap().scalbn(-22),) - ); - assert!(p_zero.bitwise_eq("0x1p-126".parse::<Single>().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::<Double>().unwrap(); - let neg_largest_denormal_f64 = "-0x1.ffffffffffffep-1023".parse::<Double>().unwrap(); - - assert!(smallest_f64.bitwise_eq("0x1p-1074".parse::<Double>().unwrap().scalbn(0),)); - assert!(neg_smallest_f64.bitwise_eq("-0x1p-1074".parse::<Double>().unwrap().scalbn(0),)); - - assert!("0x1p+1023".parse::<Double>().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::<Double>().unwrap().bitwise_eq(smallest_f64.scalbn(2096,),)); - assert!("0x1p+1023".parse::<Double>().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::<Double>() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1)) - ); - assert!( - "-0x1.ffffffffffffep-1021" - .parse::<Double>() - .unwrap() - .bitwise_eq(neg_largest_denormal_f64.scalbn(2)) - ); - - assert!( - "0x1.ffffffffffffep+1" - .parse::<Double>() - .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::<Double>() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1021)) - ); - assert!( - "0x1.ffffffffffffep-1" - .parse::<Double>() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1022)) - ); - assert!( - "0x1.ffffffffffffep+0" - .parse::<Double>() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(1023)) - ); - assert!( - "0x1.ffffffffffffep+1023" - .parse::<Double>() - .unwrap() - .bitwise_eq(largest_denormal_f64.scalbn(2046)) - ); - assert!("0x1p+974".parse::<Double>().unwrap().bitwise_eq(smallest_f64.scalbn(2048,),)); - - let random_denormal_f64 = "0x1.c60f120d9f87cp+51".parse::<Double>().unwrap(); - assert!( - "0x1.c60f120d9f87cp-972" - .parse::<Double>() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-1023)) - ); - assert!( - "0x1.c60f120d9f87cp-1" - .parse::<Double>() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-52)) - ); - assert!( - "0x1.c60f120d9f87cp-2" - .parse::<Double>() - .unwrap() - .bitwise_eq(random_denormal_f64.scalbn(-53)) - ); - assert!( - "0x1.c60f120d9f87cp+0" - .parse::<Double>() - .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::<Double>().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2097),)); - - assert!("-0x1p-1024".parse::<Double>().unwrap().bitwise_eq(neg_largest_f64.scalbn(-2048),)); - - assert!("0x1p-1073".parse::<Double>().unwrap().bitwise_eq(largest_f64.scalbn(-2097,),)); - - assert!("0x1p-1074".parse::<Double>().unwrap().bitwise_eq(largest_f64.scalbn(-2098,),)); - assert!("-0x1p-1074".parse::<Double>().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::<Double>() - .unwrap() - .bitwise_eq("0x1p+52".parse::<Double>().unwrap().scalbn(-52),) - ); - - assert!( - "0x1p-103" - .parse::<Double>() - .unwrap() - .bitwise_eq("0x1p-51".parse::<Double>().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::<Double>().unwrap(); - let neg_largest_denormal = "-0x1.ffffffffffffep-1023".parse::<Double>().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::<Double>().unwrap().bitwise_eq(frac)); - - let frac = m_one.frexp(&mut exp); - assert_eq!(1, exp); - assert!("-0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = largest_denormal.frexp(&mut exp); - assert_eq!(-1022, exp); - assert!("0x1.ffffffffffffep-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = neg_largest_denormal.frexp(&mut exp); - assert_eq!(-1022, exp); - assert!("-0x1.ffffffffffffep-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = smallest.frexp(&mut exp); - assert_eq!(-1073, exp); - assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = neg_smallest.frexp(&mut exp); - assert_eq!(-1073, exp); - assert!("-0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = largest.frexp(&mut exp); - assert_eq!(1024, exp); - assert!("0x1.fffffffffffffp-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = neg_largest.frexp(&mut exp); - assert_eq!(1024, exp); - assert!("-0x1.fffffffffffffp-1".parse::<Double>().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::<Double>().unwrap().frexp(&mut exp); - assert_eq!(-1, exp); - assert!("0x1.fffep-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = "0x1p-51".parse::<Double>().unwrap().frexp(&mut exp); - assert_eq!(-50, exp); - assert!("0x1p-1".parse::<Double>().unwrap().bitwise_eq(frac)); - - let frac = "0x1.c60f120d9f87cp+51".parse::<Double>().unwrap().frexp(&mut exp); - assert_eq!(52, exp); - assert!("0x1.c60f120d9f87cp-1".parse::<Double>().unwrap().bitwise_eq(frac)); -} - -#[test] -fn modulo() { - let mut status; - { - let f1 = "1.5".parse::<Double>().unwrap(); - let f2 = "1.0".parse::<Double>().unwrap(); - let expected = "0.5".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0.5".parse::<Double>().unwrap(); - let f2 = "1.0".parse::<Double>().unwrap(); - let expected = "0.5".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1.3333333333333p-2".parse::<Double>().unwrap(); // 0.3 - let f2 = "0x1.47ae147ae147bp-7".parse::<Double>().unwrap(); // 0.01 - // 0.009999999999999983 - let expected = "0x1.47ae147ae1471p-7".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1p64".parse::<Double>().unwrap(); // 1.8446744073709552e19 - let f2 = "1.5".parse::<Double>().unwrap(); - let expected = "1.0".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0x1p1000".parse::<Double>().unwrap(); - let f2 = "0x1p-1000".parse::<Double>().unwrap(); - let expected = "0.0".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "0.0".parse::<Double>().unwrap(); - let f2 = "1.0".parse::<Double>().unwrap(); - let expected = "0.0".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).bitwise_eq(expected)); - assert_eq!(status, Status::OK); - } - { - let f1 = "1.0".parse::<Double>().unwrap(); - let f2 = "0.0".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } - { - let f1 = "0.0".parse::<Double>().unwrap(); - let f2 = "0.0".parse::<Double>().unwrap(); - assert!(unpack!(status=, f1 % f2).is_nan()); - assert_eq!(status, Status::INVALID_OP); - } - { - let f1 = Double::INFINITY; - let f2 = "1.0".parse::<Double>().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 c769d265437..00000000000 --- 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::<DoubleDouble>().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::<DoubleDouble>().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::<DoubleDouble>().unwrap(); - assert_eq!(0x3ff0000000000000, test.to_bits()); - - // LDBL_MAX - let test = "1.79769313486231580793728971405301e+308".parse::<DoubleDouble>().unwrap(); - assert_eq!(0x7c8ffffffffffffe_7fefffffffffffff, test.to_bits()); - - // LDBL_MIN - let test = "2.00416836000897277799610805135016e-292".parse::<DoubleDouble>().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::<DoubleDouble>().unwrap(); - a = a.mul_add("3".parse::<DoubleDouble>().unwrap(), "4".parse::<DoubleDouble>().unwrap()).value; - assert_eq!(Some(Ordering::Equal), "10".parse::<DoubleDouble>().unwrap().partial_cmp(&a)); -} - -#[test] -fn ppc_double_double_round_to_integral() { - { - let a = "1.5".parse::<DoubleDouble>().unwrap(); - let a = a.round_to_integral(Round::NearestTiesToEven).value; - assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().unwrap().partial_cmp(&a)); - } - { - let a = "2.5".parse::<DoubleDouble>().unwrap(); - let a = a.round_to_integral(Round::NearestTiesToEven).value; - assert_eq!(Some(Ordering::Equal), "2".parse::<DoubleDouble>().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::<DoubleDouble>().unwrap()); - assert_eq!(0xbcb0000000000000_400f000000000000, actual.to_bits()); - } - { - let actual = float.copy_sign("-1".parse::<DoubleDouble>().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::<DoubleDouble>() - .unwrap() - .get_exact_inverse() - .unwrap() - .bitwise_eq("0.5".parse::<DoubleDouble>().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_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a7198fbf887..dd4321bea1b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2353,7 +2353,12 @@ impl Param { /// Builds a `Param` object from `ExplicitSelf`. pub fn from_self(attrs: AttrVec, eself: ExplicitSelf, eself_ident: Ident) -> Param { let span = eself.span.to(eself_ident.span); - let infer_ty = P(Ty { id: DUMMY_NODE_ID, kind: TyKind::ImplicitSelf, span, tokens: None }); + let infer_ty = P(Ty { + id: DUMMY_NODE_ID, + kind: TyKind::ImplicitSelf, + span: eself_ident.span, + tokens: None, + }); let (mutbl, ty) = match eself.node { SelfKind::Explicit(ty, mutbl) => (mutbl, ty), SelfKind::Value(mutbl) => (mutbl, infer_ty), @@ -2942,6 +2947,7 @@ pub struct StaticItem { #[derive(Clone, Encodable, Decodable, Debug)] pub struct ConstItem { pub defaultness: Defaultness, + pub generics: Generics, pub ty: P<Ty>, pub expr: Option<P<Expr>>, } @@ -3053,6 +3059,7 @@ impl ItemKind { match self { Self::Fn(box Fn { generics, .. }) | Self::TyAlias(box TyAlias { generics, .. }) + | Self::Const(box ConstItem { generics, .. }) | Self::Enum(_, generics) | Self::Struct(_, generics) | Self::Union(_, generics) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 15fe29580c2..8b9bb1df5dc 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -285,17 +285,17 @@ impl MetaItem { self.kind.value_str() } - fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> + fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> where - I: Iterator<Item = TokenTree>, + I: Iterator<Item = &'a TokenTree>, { // FIXME: Share code with `parse_path`. - let path = match tokens.next().map(TokenTree::uninterpolate) { - Some(TokenTree::Token( - Token { kind: kind @ (token::Ident(..) | token::ModSep), span }, + let path = match tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref() { + Some(&TokenTree::Token( + Token { kind: ref kind @ (token::Ident(..) | token::ModSep), span }, _, )) => 'arm: { - let mut segments = if let token::Ident(name, _) = kind { + let mut segments = if let &token::Ident(name, _) = kind { if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) = tokens.peek() { @@ -308,8 +308,8 @@ impl MetaItem { thin_vec![PathSegment::path_root(span)] }; loop { - if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = - tokens.next().map(TokenTree::uninterpolate) + if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = + tokens.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref() { segments.push(PathSegment::from_ident(Ident::new(name, span))); } else { @@ -326,7 +326,7 @@ impl MetaItem { let span = span.with_hi(segments.last().unwrap().ident.span.hi()); Path { span, segments, tokens: None } } - Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &*nt { + Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt { token::Nonterminal::NtMeta(item) => return item.meta(item.path.span), token::Nonterminal::NtPath(path) => (**path).clone(), _ => return None, @@ -354,7 +354,7 @@ impl MetaItemKind { } fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<NestedMetaItem>> { - let mut tokens = tokens.into_trees().peekable(); + let mut tokens = tokens.trees().peekable(); let mut result = ThinVec::new(); while tokens.peek().is_some() { let item = NestedMetaItem::from_tokens(&mut tokens)?; @@ -367,12 +367,12 @@ impl MetaItemKind { Some(result) } - fn name_value_from_tokens( - tokens: &mut impl Iterator<Item = TokenTree>, + fn name_value_from_tokens<'a>( + tokens: &mut impl Iterator<Item = &'a TokenTree>, ) -> Option<MetaItemKind> { match tokens.next() { Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => { - MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees()) + MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) } Some(TokenTree::Token(token, _)) => { MetaItemLit::from_token(&token).map(MetaItemKind::NameValue) @@ -381,8 +381,8 @@ impl MetaItemKind { } } - fn from_tokens( - tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>, + fn from_tokens<'a>( + tokens: &mut iter::Peekable<impl Iterator<Item = &'a TokenTree>>, ) -> Option<MetaItemKind> { match tokens.peek() { Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => { @@ -501,9 +501,9 @@ impl NestedMetaItem { self.meta_item().is_some() } - fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem> + fn from_tokens<'a, I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem> where - I: Iterator<Item = TokenTree>, + I: Iterator<Item = &'a TokenTree>, { match tokens.peek() { Some(TokenTree::Token(token, _)) @@ -513,9 +513,8 @@ impl NestedMetaItem { return Some(NestedMetaItem::Lit(lit)); } Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => { - let inner_tokens = inner_tokens.clone(); tokens.next(); - return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable()); + return NestedMetaItem::from_tokens(&mut inner_tokens.trees().peekable()); } _ => {} } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 53a9c9a046e..84b56efd325 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1149,10 +1149,11 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>( } fn visit_const_item<T: MutVisitor>( - ConstItem { defaultness, ty, expr }: &mut ConstItem, + ConstItem { defaultness, generics, ty, expr }: &mut ConstItem, visitor: &mut T, ) { visit_defaultness(defaultness, visitor); + visitor.visit_generics(generics); visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 303bdd3a307..4c920e84f86 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -226,7 +226,9 @@ fn ident_can_begin_type(name: Symbol, span: Span, is_raw: bool) -> bool { .contains(&name) } -#[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +// SAFETY: due to the `Clone` impl below, all fields of all variants other than +// `Interpolated` must impl `Copy`. +#[derive(PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum TokenKind { /* Expression-operator symbols. */ Eq, @@ -299,6 +301,19 @@ pub enum TokenKind { Eof, } +impl Clone for TokenKind { + fn clone(&self) -> Self { + // `TokenKind` would impl `Copy` if it weren't for `Interpolated`. So + // for all other variants, this implementation of `clone` is just like + // a copy. This is faster than the `derive(Clone)` version which has a + // separate path for every variant. + match self { + Interpolated(nt) => Interpolated(nt.clone()), + _ => unsafe { std::ptr::read(self) }, + } + } +} + #[derive(Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub struct Token { pub kind: TokenKind, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index ca4a739abd7..e9591c7c8db 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -13,7 +13,7 @@ //! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking //! ownership of the original. -use crate::ast::StmtKind; +use crate::ast::{AttrStyle, StmtKind}; use crate::ast_traits::{HasAttrs, HasSpan, HasTokens}; use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use crate::AttrVec; @@ -22,10 +22,11 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{self, Lrc}; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{sym, Span, Symbol, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; -use std::{fmt, iter, mem}; +use std::borrow::Cow; +use std::{cmp, fmt, iter, mem}; /// When the main Rust parser encounters a syntax-extension invocation, it /// parses the arguments to the invocation as a token tree. This is a very @@ -98,12 +99,13 @@ impl TokenTree { TokenTree::Token(Token::new(kind, span), Spacing::Joint) } - pub fn uninterpolate(self) -> TokenTree { + pub fn uninterpolate(&self) -> Cow<'_, TokenTree> { match self { - TokenTree::Token(token, spacing) => { - TokenTree::Token(token.uninterpolate().into_owned(), spacing) - } - tt => tt, + TokenTree::Token(token, spacing) => match token.uninterpolate() { + Cow::Owned(token) => Cow::Owned(TokenTree::Token(token, *spacing)), + Cow::Borrowed(_) => Cow::Borrowed(self), + }, + _ => Cow::Borrowed(self), } } } @@ -564,6 +566,92 @@ impl TokenStream { pub fn chunks(&self, chunk_size: usize) -> core::slice::Chunks<'_, TokenTree> { self.0.chunks(chunk_size) } + + /// Desugar doc comments like `/// foo` in the stream into `#[doc = + /// r"foo"]`. Modifies the `TokenStream` via `Lrc::make_mut`, but as little + /// as possible. + pub fn desugar_doc_comments(&mut self) { + if let Some(desugared_stream) = desugar_inner(self.clone()) { + *self = desugared_stream; + } + + // The return value is `None` if nothing in `stream` changed. + fn desugar_inner(mut stream: TokenStream) -> Option<TokenStream> { + let mut i = 0; + let mut modified = false; + while let Some(tt) = stream.0.get(i) { + match tt { + &TokenTree::Token( + Token { kind: token::DocComment(_, attr_style, data), span }, + _spacing, + ) => { + let desugared = desugared_tts(attr_style, data, span); + let desugared_len = desugared.len(); + Lrc::make_mut(&mut stream.0).splice(i..i + 1, desugared); + modified = true; + i += desugared_len; + } + + &TokenTree::Token(..) => i += 1, + + &TokenTree::Delimited(sp, delim, ref delim_stream) => { + if let Some(desugared_delim_stream) = desugar_inner(delim_stream.clone()) { + let new_tt = TokenTree::Delimited(sp, delim, desugared_delim_stream); + Lrc::make_mut(&mut stream.0)[i] = new_tt; + modified = true; + } + i += 1; + } + } + } + if modified { Some(stream) } else { None } + } + + fn desugared_tts(attr_style: AttrStyle, data: Symbol, span: Span) -> Vec<TokenTree> { + // Searches for the occurrences of `"#*` and returns the minimum number of `#`s + // required to wrap the text. E.g. + // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0) + // - `abc "d"` is wrapped as `r#"abc "d""#` (num_of_hashes = 1) + // - `abc "##d##"` is wrapped as `r###"abc ##"d"##"###` (num_of_hashes = 3) + let mut num_of_hashes = 0; + let mut count = 0; + for ch in data.as_str().chars() { + count = match ch { + '"' => 1, + '#' if count > 0 => count + 1, + _ => 0, + }; + num_of_hashes = cmp::max(num_of_hashes, count); + } + + // `/// foo` becomes `doc = r"foo"`. + let delim_span = DelimSpan::from_single(span); + let body = TokenTree::Delimited( + delim_span, + Delimiter::Bracket, + [ + TokenTree::token_alone(token::Ident(sym::doc, false), span), + TokenTree::token_alone(token::Eq, span), + TokenTree::token_alone( + TokenKind::lit(token::StrRaw(num_of_hashes), data, None), + span, + ), + ] + .into_iter() + .collect::<TokenStream>(), + ); + + if attr_style == AttrStyle::Inner { + vec![ + TokenTree::token_alone(token::Pound, span), + TokenTree::token_alone(token::Not, span), + body, + ] + } else { + vec![TokenTree::token_alone(token::Pound, span), body] + } + } + } } /// By-reference iterator over a [`TokenStream`], that produces `&TokenTree` @@ -595,26 +683,21 @@ impl<'t> Iterator for RefTokenTreeCursor<'t> { } } -/// Owning by-value iterator over a [`TokenStream`], that produces `TokenTree` +/// Owning by-value iterator over a [`TokenStream`], that produces `&TokenTree` /// items. -// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. +/// +/// Doesn't impl `Iterator` because Rust doesn't permit an owning iterator to +/// return `&T` from `next`; the need for an explicit lifetime in the `Item` +/// associated type gets in the way. Instead, use `next_ref` (which doesn't +/// involve associated types) for getting individual elements, or +/// `RefTokenTreeCursor` if you really want an `Iterator`, e.g. in a `for` +/// loop. #[derive(Clone)] pub struct TokenTreeCursor { pub stream: TokenStream, index: usize, } -impl Iterator for TokenTreeCursor { - type Item = TokenTree; - - fn next(&mut self) -> Option<TokenTree> { - self.stream.0.get(self.index).map(|tree| { - self.index += 1; - tree.clone() - }) - } -} - impl TokenTreeCursor { fn new(stream: TokenStream) -> Self { TokenTreeCursor { stream, index: 0 } @@ -631,15 +714,6 @@ impl TokenTreeCursor { pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { self.stream.0.get(self.index + n) } - - // Replace the previously obtained token tree with `tts`, and rewind to - // just before them. - pub fn replace_prev_and_rewind(&mut self, tts: Vec<TokenTree>) { - assert!(self.index > 0); - self.index -= 1; - let stream = Lrc::make_mut(&mut self.stream.0); - stream.splice(self.index..self.index + 1, tts); - } } #[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)] diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index eece99a3eef..bdf5143b0f7 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -62,7 +62,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { CommentKind::Block => { // Whatever happens, we skip the first line. let mut i = lines - .get(0) + .first() .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 }) .unwrap_or(0); let mut j = lines.len(); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d9de5b8e197..aed24e11c4e 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -308,8 +308,12 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { match &item.kind { ItemKind::ExternCrate(_) => {} ItemKind::Use(use_tree) => visitor.visit_use_tree(use_tree, item.id, false), - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) - | ItemKind::Const(box ConstItem { ty, expr, .. }) => { + ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + visitor.visit_ty(ty); + walk_list!(visitor, visit_expr, expr); + } + ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => { + visitor.visit_generics(generics); visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } @@ -677,7 +681,8 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, visitor.visit_ident(ident); walk_list!(visitor, visit_attribute, attrs); match kind { - AssocItemKind::Const(box ConstItem { ty, expr, .. }) => { + AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => { + visitor.visit_generics(generics); visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index dcaaaafedbe..0954cf03da9 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -286,7 +286,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf( self.lower_ty( container, - &mut ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf), + &ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf), ), self.arena.alloc_from_iter(fields.iter().map(|&ident| self.lower_ident(ident))), ), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ab68436c093..5a0474dbc01 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -231,9 +231,15 @@ impl<'hir> LoweringContext<'_, 'hir> { let (ty, body_id) = self.lower_const_item(t, span, e.as_deref()); hir::ItemKind::Static(ty, *m, body_id) } - ItemKind::Const(box ast::ConstItem { ty, expr, .. }) => { - let (ty, body_id) = self.lower_const_item(ty, span, expr.as_deref()); - hir::ItemKind::Const(ty, body_id) + ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { + let (generics, (ty, body_id)) = self.lower_generics( + generics, + Const::No, + id, + &ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + |this| this.lower_const_item(ty, span, expr.as_deref()), + ); + hir::ItemKind::Const(ty, generics, body_id) } ItemKind::Fn(box Fn { sig: FnSig { decl, header, span: fn_sig_span }, @@ -551,17 +557,6 @@ impl<'hir> LoweringContext<'_, 'hir> { for &(ref use_tree, id) in trees { let new_hir_id = self.local_def_id(id); - let mut prefix = prefix.clone(); - - // Give the segments new node-ids since they are being cloned. - for seg in &mut prefix.segments { - // Give the cloned segment the same resolution information - // as the old one (this is needed for stability checking). - let new_id = self.next_node_id(); - self.resolver.clone_res(seg.id, new_id); - seg.id = new_id; - } - // Each `use` import is an item and thus are owners of the // names in the path. Up to this point the nested import is // the current owner, since we want each desugared import to @@ -570,6 +565,9 @@ impl<'hir> LoweringContext<'_, 'hir> { self.with_hir_id_owner(id, |this| { let mut ident = *ident; + // `prefix` is lowered multiple times, but in different HIR owners. + // So each segment gets renewed `HirId` with the same + // `ItemLocalId` and the new owner. (See `lower_node_id`) let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); if let Some(attrs) = attrs { @@ -723,11 +721,23 @@ impl<'hir> LoweringContext<'_, 'hir> { let trait_item_def_id = hir_id.expect_owner(); let (generics, kind, has_default) = match &i.kind { - AssocItemKind::Const(box ConstItem { ty, expr, .. }) => { - let ty = - self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); - let body = expr.as_ref().map(|x| self.lower_const_body(i.span, Some(x))); - (hir::Generics::empty(), hir::TraitItemKind::Const(ty, body), body.is_some()) + AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => { + let (generics, kind) = self.lower_generics( + &generics, + Const::No, + i.id, + &ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + |this| { + let ty = this.lower_ty( + ty, + &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), + ); + let body = expr.as_ref().map(|x| this.lower_const_body(i.span, Some(x))); + + hir::TraitItemKind::Const(ty, body) + }, + ); + (generics, kind, expr.is_some()) } AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => { let asyncness = sig.header.asyncness; @@ -825,14 +835,19 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_attrs(hir_id, &i.attrs); let (generics, kind) = match &i.kind { - AssocItemKind::Const(box ConstItem { ty, expr, .. }) => { - let ty = - self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); - ( - hir::Generics::empty(), - hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())), - ) - } + AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( + &generics, + Const::No, + i.id, + &ImplTraitContext::Disallowed(ImplTraitPosition::Generic), + |this| { + let ty = this + .lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); + let body = this.lower_const_body(i.span, expr.as_deref()); + + hir::ImplItemKind::Const(ty, body) + }, + ), AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => { self.current_item = Some(i.span); let asyncness = sig.header.asyncness; diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 429e62c4a1c..ac750690046 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -148,10 +148,6 @@ trait ResolverAstLoweringExt { fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>; fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>; fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>>; - // Clones the resolution (if any) on 'source' and applies it - // to 'target'. Used when desugaring a `UseTreeKind::Nested` to - // multiple `UseTreeKind::Simple`s - fn clone_res(&mut self, source: NodeId, target: NodeId); fn get_label_res(&self, id: NodeId) -> Option<NodeId>; fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>; fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>; @@ -184,12 +180,6 @@ impl ResolverAstLoweringExt for ResolverAstLowering { None } - fn clone_res(&mut self, source: NodeId, target: NodeId) { - if let Some(res) = self.partial_res_map.get(&source) { - self.partial_res_map.insert(target, *res); - } - } - /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> { self.partial_res_map.get(&id).copied() @@ -1619,13 +1609,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug!(?hir_bounds); let lifetime_mapping = if in_trait { - self.arena.alloc_from_iter( - collected_lifetime_mapping - .iter() - .map(|(lifetime, def_id)| (**lifetime, *def_id)), + Some( + &*self.arena.alloc_from_iter( + collected_lifetime_mapping + .iter() + .map(|(lifetime, def_id)| (**lifetime, *def_id)), + ), ) } else { - &mut [] + None }; let opaque_ty_item = hir::OpaqueTy { @@ -2090,13 +2082,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); let lifetime_mapping = if in_trait { - self.arena.alloc_from_iter( - collected_lifetime_mapping - .iter() - .map(|(lifetime, def_id)| (**lifetime, *def_id)), + Some( + &*self.arena.alloc_from_iter( + collected_lifetime_mapping + .iter() + .map(|(lifetime, def_id)| (**lifetime, *def_id)), + ), ) } else { - &mut [] + None }; let opaque_ty_item = hir::OpaqueTy { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 418e1df5857..af594a00705 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -659,7 +659,7 @@ fn validate_generic_param_order( GenericParamKind::Type { .. } => (ParamKindOrd::TypeOrConst, ident.to_string()), GenericParamKind::Const { ty, .. } => { let ty = pprust::ty_to_string(ty); - (ParamKindOrd::TypeOrConst, format!("const {}: {}", ident, ty)) + (ParamKindOrd::TypeOrConst, format!("const {ident}: {ty}")) } }; param_idents.push((kind, ord_kind, bounds, idx, ident)); @@ -1463,15 +1463,12 @@ fn deny_equality_constraints( let Some(arg) = args.args.last() else { continue; }; - ( - format!(", {} = {}", assoc, ty), - arg.span().shrink_to_hi(), - ) + (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi()) } _ => continue, }, None => ( - format!("<{} = {}>", assoc, ty), + format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi(), ), }; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b0dbc2c2340..c4efad7caf2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -218,6 +218,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } } + if !attr.is_doc_comment() + && attr.get_normal_item().path.segments.len() == 2 + && attr.get_normal_item().path.segments[0].ident.name == sym::diagnostic + && !self.features.diagnostic_namespace + { + let msg = "`#[diagnostic]` attribute name space is experimental"; + gate_feature_post!( + self, + diagnostic_namespace, + attr.get_normal_item().path.segments[0].ident.span, + msg + ); + } // Emit errors for non-staged-api crates. if !self.features.staged_api { @@ -556,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_closures, "const closures are experimental"); gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); + gate_all!(generic_const_items, "generic const items are experimental"); if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 609920180a2..8767bbcb210 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -697,15 +697,15 @@ pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> St write!(template, "{n}").unwrap(); if p.format_options != Default::default() || p.format_trait != FormatTrait::Display { - template.push_str(":"); + template.push(':'); } if let Some(fill) = p.format_options.fill { template.push(fill); } match p.format_options.alignment { - Some(FormatAlignment::Left) => template.push_str("<"), - Some(FormatAlignment::Right) => template.push_str(">"), - Some(FormatAlignment::Center) => template.push_str("^"), + Some(FormatAlignment::Left) => template.push('<'), + Some(FormatAlignment::Right) => template.push('>'), + Some(FormatAlignment::Center) => template.push('^'), None => {} } match p.format_options.sign { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 5c01b7ea70a..d27a44f1206 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -30,10 +30,15 @@ impl<'a> State<'a> { ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::ForeignItemKind::Static(ty, mutbl, body) => { - let def = ast::Defaultness::Final; - self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); - } + ast::ForeignItemKind::Static(ty, mutbl, body) => self.print_item_const( + ident, + Some(*mutbl), + &ast::Generics::default(), + ty, + body.as_deref(), + vis, + ast::Defaultness::Final, + ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { defaultness, generics, @@ -67,6 +72,7 @@ impl<'a> State<'a> { &mut self, ident: Ident, mutbl: Option<ast::Mutability>, + generics: &ast::Generics, ty: &ast::Ty, body: Option<&ast::Expr>, vis: &ast::Visibility, @@ -82,6 +88,7 @@ impl<'a> State<'a> { }; self.word_space(leading); self.print_ident(ident); + self.print_generic_params(&generics.params); self.word_space(":"); self.print_type(ty); if body.is_some() { @@ -92,6 +99,7 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(body); } + self.print_where_clause(&generics.where_clause); self.word(";"); self.end(); // end the outer cbox } @@ -158,20 +166,21 @@ impl<'a> State<'a> { self.word(";"); } ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => { - let def = ast::Defaultness::Final; self.print_item_const( item.ident, Some(*mutbl), + &ast::Generics::default(), ty, body.as_deref(), &item.vis, - def, + ast::Defaultness::Final, ); } - ast::ItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => { + ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { self.print_item_const( item.ident, None, + generics, ty, expr.as_deref(), &item.vis, @@ -515,8 +524,16 @@ impl<'a> State<'a> { ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::AssocItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => { - self.print_item_const(ident, None, ty, expr.as_deref(), vis, *defaultness); + ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { + self.print_item_const( + ident, + None, + generics, + ty, + expr.as_deref(), + vis, + *defaultness, + ); } ast::AssocItemKind::Type(box ast::TyAlias { defaultness, diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index a4e0e773a81..a2c7e767b4c 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -37,8 +37,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { desc, ); - err.span_label(borrow_span, format!("{} is borrowed here", borrow_desc)); - err.span_label(span, format!("use of borrowed {}", borrow_desc)); + err.span_label(borrow_span, format!("{borrow_desc} is borrowed here")); + err.span_label(span, format!("use of borrowed {borrow_desc}")); err } @@ -51,8 +51,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { old_opt_via: &str, old_load_end_span: Option<Span>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let via = - |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) }; + let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") }; let mut err = struct_span_err!( self, new_loan_span, @@ -143,9 +142,9 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { ); err.span_label( new_loan_span, - format!("{} construction occurs here{}", container_name, opt_via), + format!("{container_name} construction occurs here{opt_via}"), ); - err.span_label(old_loan_span, format!("borrow occurs here{}", old_opt_via)); + err.span_label(old_loan_span, format!("borrow occurs here{old_opt_via}")); if let Some(previous_end_span) = previous_end_span { err.span_label(previous_end_span, "borrow ends here"); } @@ -173,13 +172,10 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { opt_via, kind_new, ); - err.span_label( - new_loan_span, - format!("{}borrow occurs here{}", second_borrow_desc, opt_via), - ); + err.span_label(new_loan_span, format!("{second_borrow_desc}borrow occurs here{opt_via}")); err.span_label( old_loan_span, - format!("{} construction occurs here{}", container_name, old_opt_via), + format!("{container_name} construction occurs here{old_opt_via}"), ); if let Some(previous_end_span) = previous_end_span { err.span_label(previous_end_span, "borrow from closure ends here"); @@ -199,8 +195,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { msg_old: &str, old_load_end_span: Option<Span>, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { - let via = - |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) }; + let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") }; let mut err = struct_span_err!( self, span, @@ -216,22 +211,21 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { if msg_new == "" { // If `msg_new` is empty, then this isn't a borrow of a union field. - err.span_label(span, format!("{} borrow occurs here", kind_new)); - err.span_label(old_span, format!("{} borrow occurs here", kind_old)); + err.span_label(span, format!("{kind_new} borrow occurs here")); + err.span_label(old_span, format!("{kind_old} borrow occurs here")); } else { // If `msg_new` isn't empty, then this a borrow of a union field. err.span_label( span, format!( - "{} borrow of {} -- which overlaps with {} -- occurs here", - kind_new, msg_new, msg_old, + "{kind_new} borrow of {msg_new} -- which overlaps with {msg_old} -- occurs here", ), ); err.span_label(old_span, format!("{} borrow occurs here{}", kind_old, via(msg_old))); } if let Some(old_load_end_span) = old_load_end_span { - err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old)); + err.span_label(old_load_end_span, format!("{kind_old} borrow ends here")); } err } @@ -250,8 +244,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { desc, ); - err.span_label(borrow_span, format!("{} is borrowed here", desc)); - err.span_label(span, format!("{} is assigned to here but it was already borrowed", desc)); + err.span_label(borrow_span, format!("{desc} is borrowed here")); + err.span_label(span, format!("{desc} is assigned to here but it was already borrowed")); err } @@ -330,7 +324,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { optional_adverb_for_moved: &str, moved_path: Option<String>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let moved_path = moved_path.map(|mp| format!(": `{}`", mp)).unwrap_or_default(); + let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); struct_span_err!( self, @@ -369,8 +363,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { immutable_place, immutable_section, ); - err.span_label(mutate_span, format!("cannot {}", action)); - err.span_label(immutable_span, format!("value is immutable in {}", immutable_section)); + err.span_label(mutate_span, format!("cannot {action}")); + err.span_label(immutable_span, format!("value is immutable in {immutable_section}")); err } @@ -428,7 +422,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err.span_label( span, - format!("{}s a {} data owned by the current function", return_kind, reference_desc), + format!("{return_kind}s a {reference_desc} data owned by the current function"), ); err @@ -449,8 +443,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \ which is owned by the current {scope}", ); - err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) - .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path)); + err.span_label(capture_span, format!("{borrowed_path} is borrowed here")) + .span_label(closure_span, format!("may outlive borrowed value {borrowed_path}")); err } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 1064b44d2cd..1e89a9f5144 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -360,7 +360,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { return; } let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| { - panic!("could not find BorrowIndex for location {:?}", location); + panic!("could not find BorrowIndex for location {location:?}"); }); trans.gen(index); diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 97c3e0b879a..03b90f4ab18 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -653,7 +653,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_suggestion_verbose( sugg_span.shrink_to_hi(), "consider assigning a value", - format!(" = {}", assign_value), + format!(" = {assign_value}"), Applicability::MaybeIncorrect, ); } @@ -738,7 +738,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Try to find predicates on *generic params* that would allow copying `ty` let suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { - format!(": {}.clone()", symbol) + format!(": {symbol}.clone()") } else { ".clone()".to_owned() }; @@ -1162,8 +1162,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if union_type_name != "" { err.note(format!( - "{} is a field of the union `{}`, so it overlaps the field {}", - msg_place, union_type_name, msg_borrow, + "{msg_place} is a field of the union `{union_type_name}`, so it overlaps the field {msg_borrow}", )); } @@ -1353,8 +1352,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let Some(trait_did) = tcx.trait_of_item(def_id) && tcx.is_diagnostic_item(sym::Iterator, trait_did) { err.note(format!( - "a for loop advances the iterator for you, the result is stored in `{}`.", - loop_bind + "a for loop advances the iterator for you, the result is stored in `{loop_bind}`." )); err.help("if you want to call `next` on a iterator within the loop, consider using `while let`."); } @@ -1825,7 +1823,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }, ConstraintCategory::CallArgument(None), var_or_use_span, - &format!("`{}`", name), + &format!("`{name}`"), "block", ), ( @@ -1847,7 +1845,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { region_name, category, span, - &format!("`{}`", name), + &format!("`{name}`"), "function", ), ( @@ -1921,14 +1919,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{}`", name)); + let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{name}`")); if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { let region_name = annotation.emit(self, &mut err); err.span_label( borrow_span, - format!("`{}` would have to be valid for `{}`...", name, region_name), + format!("`{name}` would have to be valid for `{region_name}`..."), ); err.span_label( @@ -1939,7 +1937,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.infcx .tcx .opt_item_name(self.mir_def_id().to_def_id()) - .map(|name| format!("function `{}`", name)) + .map(|name| format!("function `{name}`")) .unwrap_or_else(|| { match &self.infcx.tcx.def_kind(self.mir_def_id()) { DefKind::Closure => "enclosing closure", @@ -1974,7 +1972,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } else { err.span_label(borrow_span, "borrowed value does not live long enough"); - err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name)); + err.span_label(drop_span, format!("`{name}` dropped here while still borrowed")); borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { @@ -2018,22 +2016,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut err = self.cannot_borrow_across_destructor(borrow_span); let what_was_dropped = match self.describe_place(place.as_ref()) { - Some(name) => format!("`{}`", name), + Some(name) => format!("`{name}`"), None => String::from("temporary value"), }; let label = match self.describe_place(borrow.borrowed_place.as_ref()) { Some(borrowed) => format!( - "here, drop of {D} needs exclusive access to `{B}`, \ - because the type `{T}` implements the `Drop` trait", - D = what_was_dropped, - T = dropped_ty, - B = borrowed + "here, drop of {what_was_dropped} needs exclusive access to `{borrowed}`, \ + because the type `{dropped_ty}` implements the `Drop` trait" ), None => format!( - "here is drop of {D}; whose type `{T}` implements the `Drop` trait", - D = what_was_dropped, - T = dropped_ty + "here is drop of {what_was_dropped}; whose type `{dropped_ty}` implements the `Drop` trait" ), }; err.span_label(drop_span, label); @@ -2245,10 +2238,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { "local data " }; - ( - format!("{}`{}`", local_kind, place_desc), - format!("`{}` is borrowed here", place_desc), - ) + (format!("{local_kind}`{place_desc}`"), format!("`{place_desc}` is borrowed here")) } else { let root_place = self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap(); @@ -2350,9 +2340,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_suggestion_verbose( sugg_span, format!( - "to force the {} to take ownership of {} (and any \ - other referenced variables), use the `move` keyword", - kind, captured_var + "to force the {kind} to take ownership of {captured_var} (and any \ + other referenced variables), use the `move` keyword" ), suggestion, Applicability::MachineApplicable, @@ -2360,7 +2349,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match category { ConstraintCategory::Return(_) | ConstraintCategory::OpaqueType => { - let msg = format!("{} is returned here", kind); + let msg = format!("{kind} is returned here"); err.span_note(constraint_span, msg); } ConstraintCategory::CallArgument(_) => { @@ -2402,21 +2391,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( upvar_span, - format!("`{}` declared here, outside of the {} body", upvar_name, escapes_from), + format!("`{upvar_name}` declared here, outside of the {escapes_from} body"), ); - err.span_label(borrow_span, format!("borrow is only valid in the {} body", escapes_from)); + err.span_label(borrow_span, format!("borrow is only valid in the {escapes_from} body")); if let Some(name) = name { err.span_label( escape_span, - format!("reference to `{}` escapes the {} body here", name, escapes_from), + format!("reference to `{name}` escapes the {escapes_from} body here"), ); } else { - err.span_label( - escape_span, - format!("reference escapes the {} body here", escapes_from), - ); + err.span_label(escape_span, format!("reference escapes the {escapes_from} body here")); } err @@ -2697,10 +2683,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }); if let Some(Ok(instance)) = deref_target { let deref_target_ty = instance.ty(tcx, self.param_env); - err.note(format!( - "borrow occurs due to deref coercion to `{}`", - deref_target_ty - )); + err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`")); err.span_note(tcx.def_span(instance.def_id()), "deref defined here"); } } @@ -2756,7 +2739,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "cannot assign twice to immutable variable" }; if span != assigned_span && !from_arg { - err.span_label(assigned_span, format!("first assignment to {}", place_description)); + err.span_label(assigned_span, format!("first assignment to {place_description}")); } if let Some(decl) = local_decl && let Some(name) = local_name @@ -2765,7 +2748,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_suggestion( decl.source_info.span, "consider making this binding mutable", - format!("mut {}", name), + format!("mut {name}"), Applicability::MachineApplicable, ); } @@ -3226,7 +3209,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { return_span, } => { let argument_ty_name = cx.get_name_for_ty(argument_ty, 0); - diag.span_label(argument_span, format!("has type `{}`", argument_ty_name)); + diag.span_label(argument_span, format!("has type `{argument_ty_name}`")); let return_ty_name = cx.get_name_for_ty(return_ty, 0); let types_equal = return_ty_name == argument_ty_name; @@ -3253,15 +3236,14 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { // Region of return type and arguments checked to be the same earlier. let region_name = cx.get_region_name_for_ty(*return_ty, 0); for (_, argument_span) in arguments { - diag.span_label(*argument_span, format!("has lifetime `{}`", region_name)); + diag.span_label(*argument_span, format!("has lifetime `{region_name}`")); } - diag.span_label(*return_span, format!("also has lifetime `{}`", region_name,)); + diag.span_label(*return_span, format!("also has lifetime `{region_name}`",)); diag.help(format!( - "use data from the highlighted arguments which match the `{}` lifetime of \ + "use data from the highlighted arguments which match the `{region_name}` lifetime of \ the return type", - region_name, )); region_name diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 87fad9a355d..9916ebca32f 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -202,7 +202,7 @@ trait FactCell { impl<A: Debug> FactCell for A { default fn to_string(&self, _location_table: &LocationTable) -> String { - format!("{:?}", self) + format!("{self:?}") } } diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 0152d89eb83..df5e383ad40 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -353,7 +353,6 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { let tcx = self.tcx; let body = self.body; let borrow_set = self.borrow_set; - let indices = self.borrow_set.indices(); each_borrow_involving_path( self, tcx, @@ -361,7 +360,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> { location, (sd, place), borrow_set, - indices, + |_| true, |this, borrow_index, borrow| { match (rw, borrow.kind) { // Obviously an activation is compatible with its own diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ea32506fc89..317af8ae1f6 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -23,7 +23,7 @@ use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, Subdiagnost use rustc_fluent_macro::fluent_messages; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; -use rustc_index::bit_set::ChunkedBitSet; +use rustc_index::bit_set::{BitSet, ChunkedBitSet}; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::{ InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt, @@ -42,7 +42,6 @@ use rustc_session::lint::builtin::UNUSED_MUT; use rustc_span::{Span, Symbol}; use rustc_target::abi::FieldIdx; -use either::Either; use smallvec::SmallVec; use std::cell::RefCell; use std::collections::BTreeMap; @@ -1035,12 +1034,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let borrow_set = self.borrow_set.clone(); // Use polonius output if it has been enabled. - let polonius_output = self.polonius_output.clone(); - let borrows_in_scope = if let Some(polonius) = &polonius_output { + let mut polonius_output; + let borrows_in_scope = if let Some(polonius) = &self.polonius_output { let location = self.location_table.start_index(location); - Either::Left(polonius.errors_at(location).iter().copied()) + polonius_output = BitSet::new_empty(borrow_set.len()); + for &idx in polonius.errors_at(location) { + polonius_output.insert(idx); + } + &polonius_output } else { - Either::Right(flow_state.borrows.iter()) + &flow_state.borrows }; each_borrow_involving_path( @@ -1050,7 +1053,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location, (sd, place_span.0), &borrow_set, - borrows_in_scope, + |borrow_index| borrows_in_scope.contains(borrow_index), |this, borrow_index, borrow| match (rw, borrow.kind) { // Obviously an activation is compatible with its own // reservation (or even prior activating uses of same @@ -1817,8 +1820,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Subslice { .. } => { - panic!("we don't allow assignments to subslices, location: {:?}", - location); + panic!("we don't allow assignments to subslices, location: {location:?}"); } ProjectionElem::Field(..) => { @@ -2017,8 +2019,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.infcx.tcx.sess.delay_span_bug( span, format!( - "Accessing `{:?}` with the kind `{:?}` shouldn't be possible", - place, kind, + "Accessing `{place:?}` with the kind `{kind:?}` shouldn't be possible", ), ); } @@ -2306,11 +2307,10 @@ mod error { pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorGuaranteed>) { if let None = self.tainted_by_errors { - self.tainted_by_errors = Some( - self.tcx - .sess - .delay_span_bug(t.span.clone(), "diagnostic buffered but not emitted"), - ) + self.tainted_by_errors = Some(self.tcx.sess.delay_span_bug( + t.span.clone_ignoring_labels(), + "diagnostic buffered but not emitted", + )) } t.buffer(&mut self.buffered); } diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index b5014a3f479..679a19710a7 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -347,7 +347,7 @@ pub(super) fn dump_mir_results<'tcx>( for_each_region_constraint( infcx.tcx, closure_region_requirements, - &mut |msg| writeln!(out, "| {}", msg), + &mut |msg| writeln!(out, "| {msg}"), )?; writeln!(out, "|")?; } @@ -426,7 +426,7 @@ pub(super) fn dump_annotation<'tcx>( }; if !opaque_type_values.is_empty() { - err.note(format!("Inferred opaque type values:\n{:#?}", opaque_type_values)); + err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}")); } errors.buffer_non_error_diag(err); @@ -439,7 +439,7 @@ fn for_each_region_constraint<'tcx>( ) -> io::Result<()> { for req in &closure_region_requirements.outlives_requirements { let subject = match req.subject { - ClosureOutlivesSubject::Region(subject) => format!("{:?}", subject), + ClosureOutlivesSubject::Region(subject) => format!("{subject:?}"), ClosureOutlivesSubject::Ty(ty) => { format!("{:?}", ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))) } diff --git a/compiler/rustc_borrowck/src/path_utils.rs b/compiler/rustc_borrowck/src/path_utils.rs index ea9f8683ca7..ed93a560981 100644 --- a/compiler/rustc_borrowck/src/path_utils.rs +++ b/compiler/rustc_borrowck/src/path_utils.rs @@ -33,20 +33,24 @@ pub(super) fn each_borrow_involving_path<'tcx, F, I, S>( _location: Location, access_place: (AccessDepth, Place<'tcx>), borrow_set: &BorrowSet<'tcx>, - candidates: I, + is_candidate: I, mut op: F, ) where F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> Control, - I: Iterator<Item = BorrowIndex>, + I: Fn(BorrowIndex) -> bool, { let (access, place) = access_place; - // FIXME: analogous code in check_loans first maps `place` to - // its base_path. + // The number of candidates can be large, but borrows for different locals cannot conflict with + // each other, so we restrict the working set a priori. + let Some(borrows_for_place_base) = borrow_set.local_map.get(&place.local) else { return }; // check for loan restricting path P being used. Accounts for // borrows of P, P.a.b, etc. - for i in candidates { + for &i in borrows_for_place_base { + if !is_candidate(i) { + continue; + } let borrowed = &borrow_set[i]; if places_conflict::borrow_conflicts_with_place( diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 1217dcb9c40..c02f6f3b687 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -1,3 +1,55 @@ +//! The borrowck rules for proving disjointness are applied from the "root" of the +//! borrow forwards, iterating over "similar" projections in lockstep until +//! we can prove overlap one way or another. Essentially, we treat `Overlap` as +//! a monoid and report a conflict if the product ends up not being `Disjoint`. +//! +//! At each step, if we didn't run out of borrow or place, we know that our elements +//! have the same type, and that they only overlap if they are the identical. +//! +//! For example, if we are comparing these: +//! ```text +//! BORROW: (*x1[2].y).z.a +//! ACCESS: (*x1[i].y).w.b +//! ``` +//! +//! Then our steps are: +//! ```text +//! x1 | x1 -- places are the same +//! x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) +//! x1[2].y | x1[i].y -- equal or disjoint +//! *x1[2].y | *x1[i].y -- equal or disjoint +//! (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more! +//! ``` +//! +//! Because `zip` does potentially bad things to the iterator inside, this loop +//! also handles the case where the access might be a *prefix* of the borrow, e.g. +//! +//! ```text +//! BORROW: (*x1[2].y).z.a +//! ACCESS: x1[i].y +//! ``` +//! +//! Then our steps are: +//! ```text +//! x1 | x1 -- places are the same +//! x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) +//! x1[2].y | x1[i].y -- equal or disjoint +//! ``` +//! +//! -- here we run out of access - the borrow can access a part of it. If this +//! is a full deep access, then we *know* the borrow conflicts with it. However, +//! if the access is shallow, then we can proceed: +//! +//! ```text +//! x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we +//! are disjoint +//! ``` +//! +//! Our invariant is, that at each step of the iteration: +//! - If we didn't run out of access to match, our borrow and access are comparable +//! and either equal or disjoint. +//! - If we did run out of access, the borrow can access a part of it. + #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] use crate::ArtificialField; @@ -5,7 +57,7 @@ use crate::Overlap; use crate::{AccessDepth, Deep, Shallow}; use rustc_hir as hir; use rustc_middle::mir::{ - Body, BorrowKind, Local, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem, + Body, BorrowKind, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem, }; use rustc_middle::ty::{self, TyCtxt}; use std::cmp::max; @@ -48,7 +100,7 @@ pub fn places_conflict<'tcx>( /// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime /// array indices, for example) should be interpreted - this depends on what the caller wants in /// order to make the conservative choice and preserve soundness. -#[instrument(level = "debug", skip(tcx, body))] +#[inline] pub(super) fn borrow_conflicts_with_place<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -58,15 +110,24 @@ pub(super) fn borrow_conflicts_with_place<'tcx>( access: AccessDepth, bias: PlaceConflictBias, ) -> bool { + let borrow_local = borrow_place.local; + let access_local = access_place.local; + + if borrow_local != access_local { + // We have proven the borrow disjoint - further projections will remain disjoint. + return false; + } + // This Local/Local case is handled by the more general code below, but // it's so common that it's a speed win to check for it first. - if let Some(l1) = borrow_place.as_local() && let Some(l2) = access_place.as_local() { - return l1 == l2; + if borrow_place.projection.is_empty() && access_place.projection.is_empty() { + return true; } place_components_conflict(tcx, body, borrow_place, borrow_kind, access_place, access, bias) } +#[instrument(level = "debug", skip(tcx, body))] fn place_components_conflict<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -76,65 +137,10 @@ fn place_components_conflict<'tcx>( access: AccessDepth, bias: PlaceConflictBias, ) -> bool { - // The borrowck rules for proving disjointness are applied from the "root" of the - // borrow forwards, iterating over "similar" projections in lockstep until - // we can prove overlap one way or another. Essentially, we treat `Overlap` as - // a monoid and report a conflict if the product ends up not being `Disjoint`. - // - // At each step, if we didn't run out of borrow or place, we know that our elements - // have the same type, and that they only overlap if they are the identical. - // - // For example, if we are comparing these: - // BORROW: (*x1[2].y).z.a - // ACCESS: (*x1[i].y).w.b - // - // Then our steps are: - // x1 | x1 -- places are the same - // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) - // x1[2].y | x1[i].y -- equal or disjoint - // *x1[2].y | *x1[i].y -- equal or disjoint - // (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more! - // - // Because `zip` does potentially bad things to the iterator inside, this loop - // also handles the case where the access might be a *prefix* of the borrow, e.g. - // - // BORROW: (*x1[2].y).z.a - // ACCESS: x1[i].y - // - // Then our steps are: - // x1 | x1 -- places are the same - // x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ) - // x1[2].y | x1[i].y -- equal or disjoint - // - // -- here we run out of access - the borrow can access a part of it. If this - // is a full deep access, then we *know* the borrow conflicts with it. However, - // if the access is shallow, then we can proceed: - // - // x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we - // are disjoint - // - // Our invariant is, that at each step of the iteration: - // - If we didn't run out of access to match, our borrow and access are comparable - // and either equal or disjoint. - // - If we did run out of access, the borrow can access a part of it. - let borrow_local = borrow_place.local; let access_local = access_place.local; - - match place_base_conflict(borrow_local, access_local) { - Overlap::Arbitrary => { - bug!("Two base can't return Arbitrary"); - } - Overlap::EqualOrDisjoint => { - // This is the recursive case - proceed to the next element. - } - Overlap::Disjoint => { - // We have proven the borrow disjoint - further - // projections will remain disjoint. - debug!("borrow_conflicts_with_place: disjoint"); - return false; - } - } + // borrow_conflicts_with_place should have checked that. + assert_eq!(borrow_local, access_local); // loop invariant: borrow_c is always either equal to access_c or disjoint from it. for ((borrow_place, borrow_c), &access_c) in @@ -280,21 +286,6 @@ fn place_components_conflict<'tcx>( // Given that the bases of `elem1` and `elem2` are always either equal // or disjoint (and have the same type!), return the overlap situation // between `elem1` and `elem2`. -fn place_base_conflict(l1: Local, l2: Local) -> Overlap { - if l1 == l2 { - // the same local - base case, equal - debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL"); - Overlap::EqualOrDisjoint - } else { - // different locals - base case, disjoint - debug!("place_element_conflict: DISJOINT-LOCAL"); - Overlap::Disjoint - } -} - -// Given that the bases of `elem1` and `elem2` are always either equal -// or disjoint (and have the same type!), return the overlap situation -// between `elem1` and `elem2`. fn place_projection_conflict<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, diff --git a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs index 6524b594e44..4d620ac9de6 100644 --- a/compiler/rustc_borrowck/src/region_infer/dump_mir.rs +++ b/compiler/rustc_borrowck/src/region_infer/dump_mir.rs @@ -52,7 +52,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { writeln!(out, "|")?; writeln!(out, "| Inference Constraints")?; - self.for_each_constraint(tcx, &mut |msg| writeln!(out, "| {}", msg))?; + self.for_each_constraint(tcx, &mut |msg| writeln!(out, "| {msg}"))?; Ok(()) } @@ -69,7 +69,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { for region in self.definitions.indices() { let value = self.liveness_constraints.region_value_str(region); if value != "{}" { - with_msg(&format!("{:?} live at {}", region, value))?; + with_msg(&format!("{region:?} live at {value}"))?; } } @@ -81,12 +81,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { Locations::All(span) => { ("All", tcx.sess.source_map().span_to_embeddable_string(*span)) } - Locations::Single(loc) => ("Single", format!("{:?}", loc)), + Locations::Single(loc) => ("Single", format!("{loc:?}")), }; - with_msg(&format!( - "{:?}: {:?} due to {:?} at {}({}) ({:?}", - sup, sub, category, name, arg, span - ))?; + with_msg(&format!("{sup:?}: {sub:?} due to {category:?} at {name}({arg}) ({span:?}"))?; } Ok(()) diff --git a/compiler/rustc_borrowck/src/region_infer/graphviz.rs b/compiler/rustc_borrowck/src/region_infer/graphviz.rs index 2e15586e03b..a0cf22e935a 100644 --- a/compiler/rustc_borrowck/src/region_infer/graphviz.rs +++ b/compiler/rustc_borrowck/src/region_infer/graphviz.rs @@ -49,7 +49,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for RawConstraints<'a, 'tcx> { Some(dot::LabelText::LabelStr(Cow::Borrowed("box"))) } fn node_label(&'this self, n: &RegionVid) -> dot::LabelText<'this> { - dot::LabelText::LabelStr(format!("{:?}", n).into()) + dot::LabelText::LabelStr(format!("{n:?}").into()) } fn edge_label(&'this self, e: &OutlivesConstraint<'tcx>) -> dot::LabelText<'this> { dot::LabelText::LabelStr(format!("{:?}", e.locations).into()) @@ -100,7 +100,7 @@ impl<'a, 'this, 'tcx> dot::Labeller<'this> for SccConstraints<'a, 'tcx> { } fn node_label(&'this self, n: &ConstraintSccIndex) -> dot::LabelText<'this> { let nodes = &self.nodes_per_scc[*n]; - dot::LabelText::LabelStr(format!("{:?} = {:?}", n, nodes).into()) + dot::LabelText::LabelStr(format!("{n:?} = {nodes:?}").into()) } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index fbbccc58ad6..30dfb2d919a 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -259,7 +259,7 @@ fn sccs_info<'cx, 'tcx>( let mut reg_vars_to_origins_str = "region variables to origins:\n".to_string(); for (reg_var, origin) in var_to_origin_sorted.into_iter() { - reg_vars_to_origins_str.push_str(&format!("{:?}: {:?}\n", reg_var, origin)); + reg_vars_to_origins_str.push_str(&format!("{reg_var:?}: {origin:?}\n")); } debug!("{}", reg_vars_to_origins_str); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index d8e81827a3b..90bde88f792 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -339,8 +339,8 @@ fn check_opaque_type_well_formed<'tcx>( // version. let errors = ocx.select_all_or_error(); - // This is still required for many(half of the tests in ui/type-alias-impl-trait) - // tests to pass + // This is fishy, but we check it again in `check_opaque_meets_bounds`. + // Remove once we can prepopulate with known hidden types. let _ = infcx.take_opaque_types(); if errors.is_empty() { @@ -419,7 +419,7 @@ fn check_opaque_type_parameter_valid( return Err(tcx .sess .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note(spans, format!("{} used multiple times", descr)) + .span_note(spans, format!("{descr} used multiple times")) .emit()); } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index 9290e747914..d205862cd3f 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -470,7 +470,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String } push_sep(&mut result); - result.push_str(&format!("{:?}", fr)); + result.push_str(&format!("{fr:?}")); } RegionElement::PlaceholderRegion(placeholder) => { @@ -481,7 +481,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String } push_sep(&mut result); - result.push_str(&format!("{:?}", placeholder)); + result.push_str(&format!("{placeholder:?}")); } } } @@ -497,7 +497,7 @@ fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String fn push_location_range(str: &mut String, location1: Location, location2: Location) { if location1 == location2 { - str.push_str(&format!("{:?}", location1)); + str.push_str(&format!("{location1:?}")); } else { assert_eq!(location1.block, location2.block); str.push_str(&format!( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 78aa513033c..fd4a3ec1a5e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -409,18 +409,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { } if let ty::FnDef(def_id, args) = *constant.literal.ty().kind() { - // const_trait_impl: use a non-const param env when checking that a FnDef type is well formed. - // this is because the well-formedness of the function does not need to be proved to have `const` - // impls for trait bounds. let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); - let prev = self.cx.param_env; - self.cx.param_env = prev.without_const(); self.cx.normalize_and_prove_instantiated_predicates( def_id, instantiated_predicates, locations, ); - self.cx.param_env = prev; } } } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 322222ae330..79fae7f92f1 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept .suggestion = remove the value builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time - .cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead - .other = use `std::env::var("{$var}")` to read the variable at run time + .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead + .custom = use `std::env::var({$var_expr})` to read the variable at run time builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 19acefc38e3..9e66eaf73b3 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -401,7 +401,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, // should have errored above during parsing [] => unreachable!(), [(abi, _span)] => args.clobber_abis.push((*abi, full_span)), - [abis @ ..] => { + abis => { for (abi, span) in abis { args.clobber_abis.push((*abi, *span)); } @@ -575,7 +575,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl || named_pos.contains_key(&idx) || args.reg_args.contains(idx) { - let msg = format!("invalid reference to argument at index {}", idx); + let msg = format!("invalid reference to argument at index {idx}"); let mut err = ecx.struct_span_err(span, msg); err.span_label(span, "from here"); @@ -588,9 +588,9 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl "" }; let msg = match positional_args { - 0 => format!("no {}arguments were given", positional), - 1 => format!("there is 1 {}argument", positional), - x => format!("there are {} {}arguments", x, positional), + 0 => format!("no {positional}arguments were given"), + 1 => format!("there is 1 {positional}argument"), + x => format!("there are {x} {positional}arguments"), }; err.note(msg); @@ -624,7 +624,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl match args.named_args.get(&Symbol::intern(name)) { Some(&idx) => Some(idx), None => { - let msg = format!("there is no argument named `{}`", name); + let msg = format!("there is no argument named `{name}`"); let span = arg.position_span; ecx.struct_span_err( template_span @@ -697,8 +697,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl err.span_label(sp, msg); err.help(format!( "if this argument is intentionally unused, \ - consider using it in an asm comment: `\"/*{} */\"`", - help_str + consider using it in an asm comment: `\"/*{help_str} */\"`" )); err.emit(); } @@ -712,8 +711,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl } err.help(format!( "if these arguments are intentionally unused, \ - consider using them in an asm comment: `\"/*{} */\"`", - help_str + consider using them in an asm comment: `\"/*{help_str} */\"`" )); err.emit(); } diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 9ba98d0a5d1..b468abe3249 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -144,7 +144,7 @@ fn cs_clone_simple( } _ => cx.span_bug( trait_span, - format!("unexpected substructure in simple `derive({})`", name), + format!("unexpected substructure in simple `derive({name})`"), ), } } @@ -178,10 +178,10 @@ fn cs_clone( vdata = &variant.data; } EnumTag(..) | AllFieldlessEnum(..) => { - cx.span_bug(trait_span, format!("enum tags in `derive({})`", name,)) + cx.span_bug(trait_span, format!("enum tags in `derive({name})`",)) } StaticEnum(..) | StaticStruct(..) => { - cx.span_bug(trait_span, format!("associated function in `derive({})`", name)) + cx.span_bug(trait_span, format!("associated function in `derive({name})`")) } } @@ -193,7 +193,7 @@ fn cs_clone( let Some(ident) = field.name else { cx.span_bug( trait_span, - format!("unnamed field in normal struct in `derive({})`", name,), + format!("unnamed field in normal struct in `derive({name})`",), ); }; let call = subcall(cx, field); diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 3921533c84a..bcf11cb4ce9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -204,7 +204,7 @@ where let fields = fields .iter() .enumerate() - .map(|(i, &span)| getarg(cx, span, Symbol::intern(&format!("_field{}", i)), i)) + .map(|(i, &span)| getarg(cx, span, Symbol::intern(&format!("_field{i}")), i)) .collect(); cx.expr_call(trait_span, path_expr, fields) diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index a3b11309d0c..2dc20c32497 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -173,7 +173,7 @@ fn encodable_substructure( for (i, &FieldInfo { name, ref self_expr, span, .. }) in fields.iter().enumerate() { let name = match name { Some(id) => id.name, - None => Symbol::intern(&format!("_field{}", i)), + None => Symbol::intern(&format!("_field{i}")), }; let self_ref = cx.expr_addr_of(span, self_expr.clone()); let enc = diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 9865b6a72ee..6597ee3cf1b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1166,7 +1166,7 @@ impl<'a> MethodDef<'a> { .iter() .enumerate() .skip(1) - .map(|(arg_count, _selflike_arg)| format!("__arg{}", arg_count)), + .map(|(arg_count, _selflike_arg)| format!("__arg{arg_count}")), ) .collect::<Vec<String>>(); @@ -1181,7 +1181,7 @@ impl<'a> MethodDef<'a> { let get_tag_pieces = |cx: &ExtCtxt<'_>| { let tag_idents: Vec<_> = prefixes .iter() - .map(|name| Ident::from_str_and_span(&format!("{}_tag", name), span)) + .map(|name| Ident::from_str_and_span(&format!("{name}_tag"), span)) .collect(); let mut tag_exprs: Vec<_> = tag_idents @@ -1521,7 +1521,7 @@ impl<'a> TraitDef<'a> { } fn mk_pattern_ident(&self, prefix: &str, i: usize) -> Ident { - Ident::from_str_and_span(&format!("{}_{}", prefix, i), self.span) + Ident::from_str_and_span(&format!("{prefix}_{i}"), self.span) } fn create_struct_pattern_fields( @@ -1602,8 +1602,7 @@ impl<'a> TraitDef<'a> { sp, ast::CRATE_NODE_ID, format!( - "{} slice in a packed struct that derives a built-in trait", - ty + "{ty} slice in a packed struct that derives a built-in trait" ), rustc_lint_defs::BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive ); diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index bcff475f626..92da0c069e5 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -4,7 +4,7 @@ // use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{self as ast, GenericArg}; +use rustc_ast::{self as ast, AstDeref, GenericArg}; use rustc_expand::base::{self, *}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -76,27 +76,36 @@ pub fn expand_env<'cx>( }, }; - let sp = cx.with_def_site_ctxt(sp); + let span = cx.with_def_site_ctxt(sp); let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { None => { - // Use the string literal in the code in the diagnostic to avoid confusing diagnostics, - // e.g. when the literal contains escape sequences. let ast::ExprKind::Lit(ast::token::Lit { - kind: ast::token::LitKind::Str, - symbol: original_var, + kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..), + symbol, .. }) = &var_expr.kind else { unreachable!("`expr_to_string` ensures this is a string lit") }; - cx.emit_err(errors::EnvNotDefined { - span: sp, - msg: custom_msg, - var: *original_var, - help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())), - }); + + if let Some(msg_from_user) = custom_msg { + cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }); + } else if is_cargo_env_var(var.as_str()) { + cx.emit_err(errors::EnvNotDefined::CargoEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }); + } else { + cx.emit_err(errors::EnvNotDefined::CustomEnvVar { + span, + var: *symbol, + var_expr: var_expr.ast_deref(), + }); + } + return DummyResult::any(sp); } Some(value) => cx.expr_str(sp, value), @@ -104,13 +113,9 @@ pub fn expand_env<'cx>( MacEager::expr(e) } -fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp { - if var.starts_with("CARGO_") +/// Returns `true` if an environment variable from `env!` is one used by Cargo. +fn is_cargo_env_var(var: &str) -> bool { + var.starts_with("CARGO_") || var.starts_with("DEP_") || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") - { - errors::EnvNotDefinedHelp::CargoVar - } else { - errors::EnvNotDefinedHelp::Other - } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 7b2a375a822..fbf0395bb05 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs { pub(crate) span: Span, } -//#[derive(Diagnostic)] -//#[diag(builtin_macros_env_not_defined)] -pub(crate) struct EnvNotDefined { +pub(crate) struct EnvNotDefinedWithUserMessage { pub(crate) span: Span, - pub(crate) msg: Option<Symbol>, - pub(crate) var: Symbol, - pub(crate) help: Option<EnvNotDefinedHelp>, + pub(crate) msg_from_user: Symbol, } -// Hand-written implementation to support custom user messages -impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined { +// Hand-written implementation to support custom user messages. +impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage { #[track_caller] fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> { - let mut diag = if let Some(msg) = self.msg { - #[expect( - rustc::untranslatable_diagnostic, - reason = "cannot translate user-provided messages" - )] - handler.struct_diagnostic(msg.to_string()) - } else { - handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined) - }; - diag.set_arg("var", self.var); + #[expect( + rustc::untranslatable_diagnostic, + reason = "cannot translate user-provided messages" + )] + let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string()); diag.set_span(self.span); - if let Some(help) = self.help { - diag.subdiagnostic(help); - } diag } } -#[derive(Subdiagnostic)] -pub(crate) enum EnvNotDefinedHelp { +#[derive(Diagnostic)] +pub(crate) enum EnvNotDefined<'a> { + #[diag(builtin_macros_env_not_defined)] #[help(builtin_macros_cargo)] - CargoVar, - #[help(builtin_macros_other)] - Other, + CargoEnvVar { + #[primary_span] + span: Span, + var: Symbol, + var_expr: &'a rustc_ast::Expr, + }, + #[diag(builtin_macros_env_not_defined)] + #[help(builtin_macros_custom)] + CustomEnvVar { + #[primary_span] + span: Span, + var: Symbol, + var_expr: &'a rustc_ast::Expr, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 4c878785b7b..590db12a4cd 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -179,7 +179,7 @@ fn make_format_args( err.span_suggestion( unexpanded_fmt_span.shrink_to_lo(), "you might be missing a string literal to format with", - format!("\"{}\", ", sugg_fmt), + format!("\"{sugg_fmt}\", "), Applicability::MaybeIncorrect, ); } @@ -668,7 +668,7 @@ fn report_invalid_references( let num_args_desc = match args.explicit_args().len() { 0 => "no arguments were given".to_string(), 1 => "there is 1 argument".to_string(), - n => format!("there are {} arguments", n), + n => format!("there are {n} arguments"), }; let mut e; @@ -780,7 +780,7 @@ fn report_invalid_references( if num_placeholders == 1 { "is 1 argument".to_string() } else { - format!("are {} arguments", num_placeholders) + format!("are {num_placeholders} arguments") }, ), ); @@ -811,7 +811,7 @@ fn report_invalid_references( }; e = ecx.struct_span_err( span, - format!("invalid reference to positional {} ({})", arg_list, num_args_desc), + format!("invalid reference to positional {arg_list} ({num_args_desc})"), ); e.note("positional arguments are zero-based"); } diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index bd5356575ca..2fc8a076366 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -86,10 +86,7 @@ pub(crate) mod printf { '-' => c_left = true, '+' => c_plus = true, _ => { - return Err(Some(format!( - "the flag `{}` is unknown or unsupported", - c - ))); + return Err(Some(format!("the flag `{c}` is unknown or unsupported"))); } } } @@ -268,21 +265,21 @@ pub(crate) mod printf { impl Num { fn from_str(s: &str, arg: Option<&str>) -> Self { if let Some(arg) = arg { - Num::Arg(arg.parse().unwrap_or_else(|_| panic!("invalid format arg `{:?}`", arg))) + Num::Arg(arg.parse().unwrap_or_else(|_| panic!("invalid format arg `{arg:?}`"))) } else if s == "*" { Num::Next } else { - Num::Num(s.parse().unwrap_or_else(|_| panic!("invalid format num `{:?}`", s))) + Num::Num(s.parse().unwrap_or_else(|_| panic!("invalid format num `{s:?}`"))) } } fn translate(&self, s: &mut String) -> std::fmt::Result { use std::fmt::Write; match *self { - Num::Num(n) => write!(s, "{}", n), + Num::Num(n) => write!(s, "{n}"), Num::Arg(n) => { let n = n.checked_sub(1).ok_or(std::fmt::Error)?; - write!(s, "{}$", n) + write!(s, "{n}$") } Num::Next => write!(s, "*"), } @@ -626,8 +623,8 @@ pub mod shell { impl Substitution<'_> { pub fn as_str(&self) -> String { match self { - Substitution::Ordinal(n, _) => format!("${}", n), - Substitution::Name(n, _) => format!("${}", n), + Substitution::Ordinal(n, _) => format!("${n}"), + Substitution::Name(n, _) => format!("${n}"), Substitution::Escape(_) => "$$".into(), } } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 5772471931f..053f5730f6e 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -72,7 +72,7 @@ impl AllocFnFactory<'_, '_> { let mut abi_args = ThinVec::new(); let mut i = 0; let mut mk = || { - let name = Ident::from_str_and_span(&format!("arg{}", i), self.span); + let name = Ident::from_str_and_span(&format!("arg{i}"), self.span); i += 1; name }; diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index a7908373b6d..7c0b36ced96 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -179,8 +179,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { == prev_item.path.segments[0].ident.name { format!( - "only one `#[{}]` attribute is allowed on any given function", - path_str, + "only one `#[{path_str}]` attribute is allowed on any given function", ) } else { format!( diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index e613b904d2e..433da74231f 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -149,7 +149,7 @@ pub fn expand_include<'cx>( Ok(None) => { if self.p.token != token::Eof { let token = pprust::token_to_string(&self.p.token); - let msg = format!("expected item, found `{}`", token); + let msg = format!("expected item, found `{token}`"); self.p.struct_span_err(self.p.token.span, msg).emit(); } diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 6bc4f6fc1fc..1580a6f6dd3 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -255,6 +255,7 @@ pub fn expand_test_or_bench( ast::ItemKind::Const( ast::ConstItem { defaultness: ast::Defaultness::Final, + generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), // test::TestDescAndFn { expr: Some( diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 8e6c1e8ade0..652d6eca3f6 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -12,9 +12,11 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Install rustfmt + - name: Avoid installing rustc-dev run: | - rustup component add rustfmt + sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain + echo 'profile = "minimal"' >> rust-toolchain + rustfmt -v - name: Rustfmt run: | @@ -127,7 +129,7 @@ jobs: - uses: actions/checkout@v3 - name: Prepare dependencies - run: ./y.rs prepare + run: ./y.sh prepare - name: Disable JIT tests run: | @@ -136,7 +138,7 @@ jobs: - name: Test env: TARGET_TRIPLE: x86_64-unknown-linux-gnu - run: ./y.rs test --use-backend llvm + run: ./y.sh test --use-backend llvm bench: runs-on: ubuntu-latest diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock index 904233d4242..af8e43da4ea 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.lock +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -51,18 +51,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6160c0a96253993b79fb7e0983534a4515ecf666120ddf8f92068114997ebc" +checksum = "ec27af72e56235eb326b5bf2de4e70ab7c5ac1fb683a1829595badaf821607fd" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b38da5f63562e42f3c929d7c76871098e5ad12c8ab44b0659ffc529f22a5b3a" +checksum = "2231e12925e6c5f4bc9c95b62a798eea6ed669a95bc3e00f8b2adb3b7b9b7a80" dependencies = [ "bumpalo", "cranelift-bforest", @@ -81,39 +81,39 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011371e213e163b55dd9e8404b3f2d9fa52cd14dc2f3dc5b83e61ffceff126db" +checksum = "413b00b8dfb3aab85674a534677e7ca08854b503f164a70ec0634fce80996e2c" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf97dde7f5ad571161cdd203a2c9c88682ef669830aea3c14ea5d164ef8bb43" +checksum = "cd0feb9ecc8193ef5cb04f494c5bd835e5bfec4bde726e7ac0444fc9dd76229e" [[package]] name = "cranelift-control" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9a9254aee733b0f2b68e5eaaf0337ad53cb23252a056c10a35370551be8d40" +checksum = "72eedd2afcf5fee1e042eaaf18d3750e48ad0eca364a9f5971ecfdd5ef85bf71" dependencies = [ "arbitrary", ] [[package]] name = "cranelift-entity" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf39a33ee39479d1337cd9333f3c09786c5a0ca1ec509edcaf9d1346d5de0e5" +checksum = "7af19157be42671073cf8c2a52d6a4ae1e7b11f1dcb4131fede356d9f91c29dd" [[package]] name = "cranelift-frontend" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e260b92a193a0a2dccc3938f133d9532e7dcfe8d03e36bf8b7d3518c1c1793" +checksum = "c2dc7636c5fad156be7d9ae691cd1aaecd97326caf2ab534ba168056d56aa76c" dependencies = [ "cranelift-codegen", "log", @@ -123,15 +123,15 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9446c8e1aadfcdacee1a49592bc2c25d1d9bf5484782c163e7f5485c92cd3c1c" +checksum = "c1111aea4fb6fade5779903f184249a3fc685a799fe4ec59126f9af59c7c2a74" [[package]] name = "cranelift-jit" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "689a6df165d0f860c1e1a3d53c28944e2743c3e9ee4c678cf190fe60ad7a6ef5" +checksum = "dadf88076317f6286ec77ebbe65978734fb43b6befdc96f52ff4c4c511841644" dependencies = [ "anyhow", "cranelift-codegen", @@ -149,9 +149,9 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b1402d6ff1695b429536b2eaa126db560fc94c375ed0e9cfb15051fc07427f7" +checksum = "c6bae8a82dbf82241b1083e57e06870d2c2bdc9852727be99d58477513816953" dependencies = [ "anyhow", "cranelift-codegen", @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac916f3c5aff4b817e42fc2e682292b931495b3fe2603d5e3c3cf602d74e344" +checksum = "1ecfc01a634448468a698beac433d98040033046678a0eed3ca39a3a9f63ae86" dependencies = [ "cranelift-codegen", "libc", @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.96.1" +version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23860f4cd064017f2108e6bc5d25660a77cd6eea77f1ac0756870a00abb12e93" +checksum = "0ee14a7276999f0dcaae2de84043e2c2de50820fb89b3db56fab586a4ad26734" dependencies = [ "anyhow", "cranelift-codegen", @@ -194,6 +194,12 @@ dependencies = [ ] [[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + +[[package]] name = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -206,7 +212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] @@ -226,6 +232,12 @@ dependencies = [ ] [[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -236,6 +248,16 @@ dependencies = [ ] [[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] name = "libc" version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -283,7 +305,7 @@ checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" dependencies = [ "crc32fast", "hashbrown 0.13.2", - "indexmap", + "indexmap 1.9.3", "memchr", ] @@ -295,9 +317,9 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "regalloc2" -version = "0.8.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a52e724646c6c0800fc456ec43b4165d2f91fba88ceaca06d9e0b400023478" +checksum = "5b4dcbd3a2ae7fb94b5813fa0e957c6ab51bf5d0a8ee1b69e0c2d0f1e6eb8485" dependencies = [ "hashbrown 0.13.2", "log", @@ -335,7 +357,7 @@ dependencies = [ "cranelift-native", "cranelift-object", "gimli", - "indexmap", + "indexmap 2.0.0", "libloading", "object", "smallvec", @@ -374,9 +396,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasmtime-jit-icache-coherence" -version = "9.0.1" +version = "11.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d90933b781e1cef7656baed671c7a90bdba0c1c694e04fdd4124419308f5cbb" +checksum = "e34eb67f0829a5614ec54716c8e0c9fe68fab7b9df3686c85f719c9d247f7169" dependencies = [ "cfg-if", "libc", diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml index 1c1f2d8577b..8ded81d7399 100644 --- a/compiler/rustc_codegen_cranelift/Cargo.toml +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -8,17 +8,17 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { version = "0.96.1", features = ["unwind", "all-arch"] } -cranelift-frontend = { version = "0.96.1" } -cranelift-module = { version = "0.96.1" } -cranelift-native = { version = "0.96.1" } -cranelift-jit = { version = "0.96.1", optional = true } -cranelift-object = { version = "0.96.1" } +cranelift-codegen = { version = "0.98", features = ["unwind", "all-arch"] } +cranelift-frontend = { version = "0.98" } +cranelift-module = { version = "0.98" } +cranelift-native = { version = "0.98" } +cranelift-jit = { version = "0.98", optional = true } +cranelift-object = { version = "0.98" } target-lexicon = "0.12.0" gimli = { version = "0.27.2", default-features = false, features = ["write"]} object = { version = "0.30.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } -indexmap = "1.9.3" +indexmap = "2.0.0" libloading = { version = "0.7.3", optional = true } smallvec = "1.8.1" diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index 9469feea0cb..62eaef359af 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -65,12 +65,12 @@ to `./build/host/stage2/bin/`. Note that you would need to do this every time yo 5. Copy cargo from another toolchain: `cp $(rustup which cargo) .build/<your hostname triple>/stage2/bin/cargo` * Another option is to build it at step 3 and copy with other executables at step 4. 6. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`. -7. (Windows only) compile y.rs: `rustc +stage2 -O y.rs`. -8. You need to prefix every `./y.rs` (or `y` if you built `y.rs`) command by `rustup run stage2` to make cg_clif use your local changes in rustc. +7. (Windows only) compile the build system: `rustc +stage2 -O build_system/main.rs -o y.exe`. +8. You need to prefix every `./y.sh` (or `y` if you built `build_system/main.rs` as `y`) command by `rustup run stage2` to make cg_clif use your local changes in rustc. - * `rustup run stage2 ./y.rs prepare` - * `rustup run stage2 ./y.rs build` - * (Optional) run tests: `rustup run stage2 ./y.rs test` + * `rustup run stage2 ./y.sh prepare` + * `rustup run stage2 ./y.sh build` + * (Optional) run tests: `rustup run stage2 ./y.sh test` 9. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`. ## Configuration diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs index 2bb11800034..cec608ea042 100644 --- a/compiler/rustc_codegen_cranelift/build_system/bench.rs +++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs @@ -1,4 +1,5 @@ use std::env; +use std::io::Write; use std::path::Path; use super::path::{Dirs, RelPath}; @@ -30,6 +31,12 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { let bench_runs = env::var("BENCH_RUNS").unwrap_or_else(|_| "10".to_string()).parse().unwrap(); + let mut gha_step_summary = if let Ok(file) = std::env::var("GITHUB_STEP_SUMMARY") { + Some(std::fs::OpenOptions::new().append(true).open(file).unwrap()) + } else { + None + }; + eprintln!("[BENCH COMPILE] ebobby/simple-raytracer"); let cargo_clif = RelPath::DIST .to_path(dirs) @@ -60,36 +67,64 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) { target_dir = target_dir.display(), ); + let bench_compile_markdown = RelPath::DIST.to_path(dirs).join("bench_compile.md"); + let bench_compile = hyperfine_command( 1, bench_runs, Some(&clean_cmd), - &[&llvm_build_cmd, &clif_build_cmd, &clif_build_opt_cmd], + &[ + ("cargo build", &llvm_build_cmd), + ("cargo-clif build", &clif_build_cmd), + ("cargo-clif build --release", &clif_build_opt_cmd), + ], + &bench_compile_markdown, ); spawn_and_wait(bench_compile); + if let Some(gha_step_summary) = gha_step_summary.as_mut() { + gha_step_summary.write_all(b"## Compile ebobby/simple-raytracer\n\n").unwrap(); + gha_step_summary.write_all(&std::fs::read(bench_compile_markdown).unwrap()).unwrap(); + gha_step_summary.write_all(b"\n").unwrap(); + } + eprintln!("[BENCH RUN] ebobby/simple-raytracer"); + let bench_run_markdown = RelPath::DIST.to_path(dirs).join("bench_run.md"); + + let raytracer_cg_llvm = Path::new(".").join(get_file_name( + &bootstrap_host_compiler.rustc, + "raytracer_cg_llvm", + "bin", + )); + let raytracer_cg_clif = Path::new(".").join(get_file_name( + &bootstrap_host_compiler.rustc, + "raytracer_cg_clif", + "bin", + )); + let raytracer_cg_clif_opt = Path::new(".").join(get_file_name( + &bootstrap_host_compiler.rustc, + "raytracer_cg_clif_opt", + "bin", + )); let mut bench_run = hyperfine_command( 0, bench_runs, None, &[ - Path::new(".") - .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_llvm", "bin")) - .to_str() - .unwrap(), - Path::new(".") - .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif", "bin")) - .to_str() - .unwrap(), - Path::new(".") - .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif_opt", "bin")) - .to_str() - .unwrap(), + ("", raytracer_cg_llvm.to_str().unwrap()), + ("", raytracer_cg_clif.to_str().unwrap()), + ("", raytracer_cg_clif_opt.to_str().unwrap()), ], + &bench_run_markdown, ); bench_run.current_dir(RelPath::BUILD.to_path(dirs)); spawn_and_wait(bench_run); + + if let Some(gha_step_summary) = gha_step_summary.as_mut() { + gha_step_summary.write_all(b"## Run ebobby/simple-raytracer\n\n").unwrap(); + gha_step_summary.write_all(&std::fs::read(bench_run_markdown).unwrap()).unwrap(); + gha_step_summary.write_all(b"\n").unwrap(); + } } diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs index 6855c1a7fc5..1c5db23299d 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use super::path::{Dirs, RelPath}; use super::rustc_info::get_file_name; -use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler}; +use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler, LogGroup}; pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif"); @@ -13,6 +13,8 @@ pub(crate) fn build_backend( bootstrap_host_compiler: &Compiler, use_unstable_features: bool, ) -> PathBuf { + let _group = LogGroup::guard("Build backend"); + let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs); maybe_incremental(&mut cmd); diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs index 74bba9ed5eb..04097936d03 100644 --- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs +++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs @@ -6,6 +6,7 @@ use super::path::{Dirs, RelPath}; use super::rustc_info::get_file_name; use super::utils::{ maybe_incremental, remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler, + LogGroup, }; use super::{CodegenBackend, SysrootKind}; @@ -22,6 +23,8 @@ pub(crate) fn build_sysroot( rustup_toolchain_name: Option<&str>, target_triple: String, ) -> Compiler { + let _guard = LogGroup::guard("Build sysroot"); + eprintln!("[BUILD] sysroot {:?}", sysroot_kind); DIST_DIR.ensure_fresh(dirs); @@ -251,7 +254,10 @@ fn build_clif_sysroot_for_triple( rustflags .push_str(&format!(" --sysroot {}", RTSTARTUP_SYSROOT.to_path(dirs).to_str().unwrap())); if channel == "release" { - rustflags.push_str(" -Zmir-opt-level=3"); + // Incremental compilation by default disables mir inlining. This leads to both a decent + // compile perf and a significant runtime perf regression. As such forcefully enable mir + // inlining. + rustflags.push_str(" -Zinline-mir"); } compiler.rustflags += &rustflags; let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs); diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs index e31e39a483f..3ee2e8f4a4e 100644 --- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs +++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs @@ -27,6 +27,7 @@ pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) { STDLIB_SRC.to_path(dirs).join("Cargo.toml"), r#" [workspace] +resolver = "1" members = ["./library/sysroot"] [patch.crates-io] diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 08d8f708c7d..0254d18cf7c 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -3,7 +3,7 @@ use super::config; use super::path::{Dirs, RelPath}; use super::prepare::{apply_patches, GitRepo}; use super::rustc_info::get_default_sysroot; -use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler}; +use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler, LogGroup}; use super::{CodegenBackend, SysrootKind}; use std::env; use std::ffi::OsStr; @@ -21,6 +21,7 @@ struct TestCase { enum TestCaseCmd { Custom { func: &'static dyn Fn(&TestRunner<'_>) }, BuildLib { source: &'static str, crate_types: &'static str }, + BuildBin { source: &'static str }, BuildBinAndRun { source: &'static str, args: &'static [&'static str] }, JitBin { source: &'static str, args: &'static str }, } @@ -39,6 +40,10 @@ impl TestCase { Self { config, cmd: TestCaseCmd::BuildLib { source, crate_types } } } + const fn build_bin(config: &'static str, source: &'static str) -> Self { + Self { config, cmd: TestCaseCmd::BuildBin { source } } + } + const fn build_bin_and_run( config: &'static str, source: &'static str, @@ -92,6 +97,7 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ TestCase::build_bin_and_run("aot.float-minmax-pass", "example/float-minmax-pass.rs", &[]), TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]), TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]), + TestCase::build_bin("aot.issue-59326", "example/issue-59326.rs"), ]; // FIXME(rust-random/rand#1293): Newer rand versions fail to test on Windows. Update once this is @@ -119,8 +125,8 @@ pub(crate) static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github( "rust-lang", "portable-simd", - "ad8afa8c81273b3b49acbea38cd3bcf17a34cf2b", - "800548f8000e31bd", + "7c7dbe0c505ccbc02ff30c1e37381ab1d47bf46f", + "5bcc9c544f6fa7bd", "portable-simd", ); @@ -380,15 +386,17 @@ impl<'a> TestRunner<'a> { let tag = tag.to_uppercase(); let is_jit_test = tag == "JIT"; - if !config::get_bool(config) + let _guard = if !config::get_bool(config) || (is_jit_test && !self.jit_supported) || self.skip_tests.contains(&config) { eprintln!("[{tag}] {testname} (skipped)"); continue; } else { + let guard = LogGroup::guard(&format!("[{tag}] {testname}")); eprintln!("[{tag}] {testname}"); - } + guard + }; match *cmd { TestCaseCmd::Custom { func } => func(self), @@ -405,6 +413,13 @@ impl<'a> TestRunner<'a> { ]); } } + TestCaseCmd::BuildBin { source } => { + if self.use_unstable_features { + self.run_rustc([source]); + } else { + self.run_rustc([source, "--cfg", "no_unstable_features"]); + } + } TestCaseCmd::BuildBinAndRun { source, args } => { if self.use_unstable_features { self.run_rustc([source]); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index 41fc366e290..97c82d501c5 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -3,6 +3,7 @@ use std::fs; use std::io::{self, Write}; use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; +use std::sync::atomic::{AtomicBool, Ordering}; use super::path::{Dirs, RelPath}; @@ -136,10 +137,13 @@ pub(crate) fn hyperfine_command( warmup: u64, runs: u64, prepare: Option<&str>, - cmds: &[&str], + cmds: &[(&str, &str)], + markdown_export: &Path, ) -> Command { let mut bench = Command::new("hyperfine"); + bench.arg("--export-markdown").arg(markdown_export); + if warmup != 0 { bench.arg("--warmup").arg(warmup.to_string()); } @@ -152,7 +156,12 @@ pub(crate) fn hyperfine_command( bench.arg("--prepare").arg(prepare); } - bench.args(cmds); + for &(name, cmd) in cmds { + if name != "" { + bench.arg("-n").arg(name); + } + bench.arg(cmd); + } bench } @@ -167,6 +176,8 @@ pub(crate) fn git_command<'a>(repo_dir: impl Into<Option<&'a Path>>, cmd: &str) .arg("user.email=dummy@example.com") .arg("-c") .arg("core.autocrlf=false") + .arg("-c") + .arg("commit.gpgSign=false") .arg(cmd); if let Some(repo_dir) = repo_dir.into() { git_cmd.current_dir(repo_dir); @@ -259,6 +270,33 @@ pub(crate) fn is_ci_opt() -> bool { env::var("CI_OPT").is_ok() } +static IN_GROUP: AtomicBool = AtomicBool::new(false); +pub(crate) struct LogGroup { + is_gha: bool, +} + +impl LogGroup { + pub(crate) fn guard(name: &str) -> LogGroup { + let is_gha = env::var("GITHUB_ACTIONS").is_ok(); + + assert!(!IN_GROUP.swap(true, Ordering::SeqCst)); + if is_gha { + eprintln!("::group::{name}"); + } + + LogGroup { is_gha } + } +} + +impl Drop for LogGroup { + fn drop(&mut self) { + if self.is_gha { + eprintln!("::endgroup::"); + } + IN_GROUP.store(false, Ordering::SeqCst); + } +} + pub(crate) fn maybe_incremental(cmd: &mut Command) { if is_ci() || std::env::var("CARGO_BUILD_INCREMENTAL").map_or(false, |val| val == "false") { // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index d6e3924a24d..fa1c9f4259c 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -41,6 +41,7 @@ aot.track-caller-attribute aot.float-minmax-pass aot.mod_bench aot.issue-72793 +aot.issue-59326 testsuite.extended_sysroot test.rust-random/rand diff --git a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs index b8f901d1ba1..80a2776ca1e 100644 --- a/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs +++ b/compiler/rustc_codegen_cranelift/example/float-minmax-pass.rs @@ -22,7 +22,7 @@ fn main() { #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))] let nan = f32::NAN; - // MIPS hardware treats f32::NAN as SNAN. Clear the signaling bit. + // MIPS hardware except MIPS R6 treats f32::NAN as SNAN. Clear the signaling bit. // See https://github.com/rust-lang/rust/issues/52746. #[cfg(any(target_arch = "mips", target_arch = "mips64"))] let nan = f32::from_bits(f32::NAN.to_bits() - 1); diff --git a/compiler/rustc_codegen_cranelift/example/issue-59326.rs b/compiler/rustc_codegen_cranelift/example/issue-59326.rs new file mode 100644 index 00000000000..70b7c94e15c --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/issue-59326.rs @@ -0,0 +1,27 @@ +// Based on https://github.com/rust-lang/rust/blob/689511047a75a30825e367d4fd45c74604d0b15e/tests/ui/issues/issue-59326.rs#L1 +// check-pass +trait Service { + type S; +} + +trait Framing { + type F; +} + +impl Framing for () { + type F = (); +} + +trait HttpService<F: Framing>: Service<S = F::F> {} + +type BoxService = Box<dyn HttpService<(), S = ()>>; + +fn build_server<F: FnOnce() -> BoxService>(_: F) {} + +fn make_server<F: Framing>() -> Box<dyn HttpService<F, S = F::F>> { + unimplemented!() +} + +fn main() { + build_server(|| make_server()) +} diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index 79ca4c03985..9ecc4c5dd5b 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -547,7 +547,9 @@ impl<T> Box<T> { impl<T: ?Sized, A> Drop for Box<T, A> { fn drop(&mut self) { // inner value is dropped by compiler - libc::free(self.0.pointer.0 as *mut u8); + unsafe { + libc::free(self.0.pointer.0 as *mut u8); + } } } diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 1bf0ff64c92..490cc2404f6 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -1,4 +1,12 @@ -#![feature(core_intrinsics, generators, generator_trait, is_sorted, repr_simd)] +#![feature( + core_intrinsics, + generators, + generator_trait, + is_sorted, + repr_simd, + tuple_trait, + unboxed_closures +)] #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; @@ -155,12 +163,34 @@ fn main() { } foo(I64X2(0, 0)); + + transmute_fat_pointer(); + + rust_call_abi(); } fn panic(_: u128) { panic!(); } +use std::mem::transmute; + +#[cfg(target_pointer_width = "32")] +type TwoPtrs = i64; +#[cfg(target_pointer_width = "64")] +type TwoPtrs = i128; + +fn transmute_fat_pointer() -> TwoPtrs { + unsafe { transmute::<_, TwoPtrs>("true !") } +} + +extern "rust-call" fn rust_call_abi_callee<T: std::marker::Tuple>(_: T) {} + +fn rust_call_abi() { + rust_call_abi_callee(()); + rust_call_abi_callee((1, 2)); +} + #[repr(simd)] struct I64X2(i64, i64); diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 1d5479bedde..a650e10110b 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -10,6 +10,18 @@ Cranelift doesn't support them yet library/core/tests/atomic.rs | 4 --- 4 files changed, 4 insertions(+), 50 deletions(-) +diff --git a/lib.rs b/lib.rs +index 897a5e9..331f66f 100644 +--- a/lib.rs ++++ b/lib.rs +@@ -93,7 +93,6 @@ + #![feature(const_option)] + #![feature(const_option_ext)] + #![feature(const_result)] +-#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] + #![feature(int_roundings)] + #![feature(slice_group_by)] + #![feature(split_array)] diff --git a/atomic.rs b/atomic.rs index b735957..ea728b6 100644 --- a/atomic.rs diff --git a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch index 45f73f36b93..646928893e9 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-stdlib-128bit-atomic-operations.patch @@ -38,9 +38,9 @@ diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index d9de37e..8293fce 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -2234,46 +2234,6 @@ atomic_int! { - "AtomicU64::new(0)", - u64 AtomicU64 ATOMIC_U64_INIT +@@ -2996,42 +2996,6 @@ atomic_int! { + 8, + u64 AtomicU64 } -#[cfg(target_has_atomic_load_store = "128")] -atomic_int! { @@ -53,14 +53,12 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), -- unstable(feature = "integer_atomics", issue = "99069"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicI128"), - "i128", - "#![feature(integer_atomics)]\n\n", - atomic_min, atomic_max, - 16, -- "AtomicI128::new(0)", -- i128 AtomicI128 ATOMIC_I128_INIT +- i128 AtomicI128 -} -#[cfg(target_has_atomic_load_store = "128")] -atomic_int! { @@ -73,16 +71,15 @@ index d9de37e..8293fce 100644 - unstable(feature = "integer_atomics", issue = "99069"), - unstable(feature = "integer_atomics", issue = "99069"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), -- unstable(feature = "integer_atomics", issue = "99069"), - cfg_attr(not(test), rustc_diagnostic_item = "AtomicU128"), - "u128", - "#![feature(integer_atomics)]\n\n", - atomic_umin, atomic_umax, - 16, -- "AtomicU128::new(0)", -- u128 AtomicU128 ATOMIC_U128_INIT +- u128 AtomicU128 -} + #[cfg(target_has_atomic_load_store = "ptr")] macro_rules! atomic_int_ptr_sized { ( $($target_pointer_width:literal $align:literal)* ) => { $( -- diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml index 1dde9e54d7e..aea47bdfba2 100644 --- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml +++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ "compiler_builtins", "gimli", @@ -35,6 +35,12 @@ dependencies = [ ] [[package]] +name = "allocator-api2" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9" + +[[package]] name = "auxv" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -68,9 +74,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.93" +version = "0.1.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76630810d973ecea3dbf611e1b7aecfb1012751ef1ff8de3998f89014a166781" +checksum = "6866e0f3638013234db3c89ead7a14d278354338e7237257407500009012b23f" dependencies = [ "cc", "rustc-std-workspace-core", @@ -145,10 +151,11 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" dependencies = [ + "allocator-api2", "compiler_builtins", "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -156,9 +163,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -186,9 +193,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", "compiler_builtins", @@ -198,9 +205,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.4" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "compiler_builtins", "memchr", diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index fa3a10b9adc..34514658359 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-06-15" +channel = "nightly-2023-07-22" components = ["rust-src", "rustc-dev", "llvm-tools"] diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs index 99b97be24e6..f73b2012684 100644 --- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs +++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs @@ -40,14 +40,22 @@ fn main() { "cargo" }; - let args: Vec<_> = match env::args().nth(1).as_deref() { + let mut args = env::args().skip(1).collect::<Vec<_>>(); + if args.get(0).map(|arg| &**arg) == Some("clif") { + // Avoid infinite recursion when invoking `cargo-clif` as cargo subcommand using + // `cargo clif`. + args.remove(0); + } + + let args: Vec<_> = match args.get(0).map(|arg| &**arg) { Some("jit") => { env::set_var( "RUSTFLAGS", env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic", ); + args.remove(0); IntoIterator::into_iter(["rustc".to_string()]) - .chain(env::args().skip(2)) + .chain(args) .chain([ "--".to_string(), "-Zunstable-options".to_string(), @@ -60,8 +68,9 @@ fn main() { "RUSTFLAGS", env::var("RUSTFLAGS").unwrap_or(String::new()) + " -Cprefer-dynamic", ); + args.remove(0); IntoIterator::into_iter(["rustc".to_string()]) - .chain(env::args().skip(2)) + .chain(args) .chain([ "--".to_string(), "-Zunstable-options".to_string(), @@ -69,7 +78,7 @@ fn main() { ]) .collect() } - _ => env::args().skip(1).collect(), + _ => args, }; #[cfg(unix)] diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 15b16b42be5..e6bbac647e5 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -10,7 +10,8 @@ git fetch git checkout -- . git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')" -git -c user.name=Dummy -c user.email=dummy@example.com am ../patches/*-stdlib-*.patch +git -c user.name=Dummy -c user.email=dummy@example.com -c commit.gpgSign=false \ + am ../patches/*-stdlib-*.patch git apply - <<EOF diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml @@ -51,7 +52,7 @@ popd # FIXME remove once inline asm is fully supported export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc" -export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd download/sysroot/sysroot_src; pwd)" +export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd build/stdlib; pwd)" # Allow the testsuite to use llvm tools host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index a7920cc54ea..83cbe0db633 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -32,6 +32,8 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR # missing features # ================ +rm -r tests/run-make/comment-section # cg_clif doesn't yet write the .comment section + # requires stack unwinding # FIXME add needs-unwind to this test rm -r tests/run-make/libtest-junit @@ -98,8 +100,11 @@ rm -r tests/run-make/sepcomp-inlining # same rm -r tests/run-make/sepcomp-separate # same rm -r tests/run-make/sepcomp-cci-copies # same rm -r tests/run-make/volatile-intrinsics # same +rm -r tests/run-make/llvm-ident # same +rm -r tests/run-make/no-builtins-attribute # same rm tests/ui/abi/stack-protector.rs # requires stack protector support rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes +rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific # giving different but possibly correct results # ============================================= @@ -118,6 +123,7 @@ rm tests/ui/suggestions/derive-trait-for-method-call.rs # same rm tests/ui/typeck/issue-46112.rs # same rm tests/ui/consts/const_cmp_type_id.rs # same rm tests/ui/consts/issue-73976-monomorphic.rs # same +rm tests/ui/rfcs/rfc-3348-c-string-literals/non-ascii.rs # same # rustdoc-clif passes extra args, suppressing the help message when no args are passed rm -r tests/run-make/issue-88756-default-output @@ -143,6 +149,8 @@ rm -r tests/run-make/used # same rm -r tests/run-make/no-alloc-shim rm -r tests/run-make/emit-to-stdout +rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported + # bugs in the test suite # ====================== rm tests/ui/backtrace.rs # TODO warning @@ -150,6 +158,8 @@ rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd +rm tests/ui/panic-handler/weak-lang-item-2.rs # Will be fixed by #113568 + cp ../dist/bin/rustdoc-clif ../dist/bin/rustdoc # some tests expect bin/rustdoc to exist # prevent $(RUSTDOC) from picking up the sysroot built by x.py. It conflicts with the one used by diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index eddb479073c..2c038f22ca9 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -445,9 +445,14 @@ pub(crate) fn codegen_terminator_call<'tcx>( // Unpack arguments tuple for closures let mut args = if fn_sig.abi() == Abi::RustCall { - assert_eq!(args.len(), 2, "rust-call abi requires two arguments"); - let self_arg = codegen_call_argument_operand(fx, &args[0]); - let pack_arg = codegen_call_argument_operand(fx, &args[1]); + let (self_arg, pack_arg) = match args { + [pack_arg] => (None, codegen_call_argument_operand(fx, pack_arg)), + [self_arg, pack_arg] => ( + Some(codegen_call_argument_operand(fx, self_arg)), + codegen_call_argument_operand(fx, pack_arg), + ), + _ => panic!("rust-call abi requires one or two arguments"), + }; let tupled_arguments = match pack_arg.value.layout().ty.kind() { ty::Tuple(ref tupled_arguments) => tupled_arguments, @@ -455,7 +460,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( }; let mut args = Vec::with_capacity(1 + tupled_arguments.len()); - args.push(self_arg); + args.extend(self_arg); for i in 0..tupled_arguments.len() { args.push(CallArgument { value: pack_arg.value.value_field(fx, FieldIdx::new(i)), diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index e05f2146f0c..522dd7189fe 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -7,6 +7,8 @@ use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; use cranelift_codegen::ir::UserFuncName; +use cranelift_codegen::CodegenError; +use cranelift_module::ModuleError; use crate::constant::ConstantCx; use crate::debuginfo::FunctionDebugContext; @@ -172,7 +174,21 @@ pub(crate) fn compile_fn( // Define function cx.profiler.generic_activity("define function").run(|| { context.want_disasm = cx.should_write_ir; - module.define_function(codegened_func.func_id, context).unwrap(); + match module.define_function(codegened_func.func_id, context) { + Ok(()) => {} + Err(ModuleError::Compilation(CodegenError::ImplLimitExceeded)) => { + let handler = rustc_session::EarlyErrorHandler::new( + rustc_session::config::ErrorOutputType::default(), + ); + handler.early_error(format!( + "backend implementation limit exceeded while compiling {name}", + name = codegened_func.symbol_name + )); + } + Err(err) => { + panic!("Error while defining {name}: {err:?}", name = codegened_func.symbol_name); + } + } }); if cx.should_write_ir { @@ -356,7 +372,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { codegen_panic_inner( fx, - rustc_hir::LangItem::PanicBoundsCheck, + rustc_hir::LangItem::PanicMisalignedPointerDereference, &[required, found, location], source_info.span, ); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs index 1b454b6667c..50bc7a127af 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -165,7 +165,7 @@ impl FunctionDebugContext { for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() { debug_context.dwarf.unit.line_program.row().address_offset = u64::from(start); if !loc.is_default() { - let source_loc = *self.source_loc_set.get_index(loc.bits() as usize).unwrap(); + let source_loc = self.source_loc_set[loc.bits() as usize]; create_row_for_span(debug_context, source_loc); } else { create_row_for_span(debug_context, self.function_source_loc); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 3a7421d8b30..8a4b1cccf14 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -38,7 +38,7 @@ pub(crate) struct DebugContext { pub(crate) struct FunctionDebugContext { entry_id: UnitEntryId, function_source_loc: (FileId, u64, u64), - source_loc_set: indexmap::IndexSet<(FileId, u64, u64)>, + source_loc_set: IndexSet<(FileId, u64, u64)>, } impl DebugContext { diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 41e24acefbe..3ea38842148 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -114,9 +114,9 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { .iter() .map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter()) .flatten() - .collect::<FxHashMap<_, (_, _)>>() + .collect::<FxHashMap<_, _>>() .into_iter() - .collect::<Vec<(_, (_, _))>>(); + .collect::<Vec<(_, _)>>(); tcx.sess.time("codegen mono items", || { super::predefine_mono_items(tcx, &mut jit_module, &mono_items); diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 18162fb5ab2..fdd27a454e0 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -18,6 +18,20 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( // Spin loop hint } + // Used by is_x86_feature_detected!(); + "llvm.x86.xgetbv" => { + // FIXME use the actual xgetbv instruction + intrinsic_args!(fx, args => (v); intrinsic); + + let v = v.load_scalar(fx); + + // As of writing on XCR0 exists + fx.bcx.ins().trapnz(v, TrapCode::UnreachableCodeReached); + + let res = fx.bcx.ins().iconst(types::I64, 1 /* bit 0 must be set */); + ret.write_cvalue(fx, CValue::by_val(res, fx.layout_of(fx.tcx.types.i64))); + } + // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" @@ -53,7 +67,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32)); ret.write_cvalue(fx, res); } - "llvm.x86.sse2.cmp.ps" | "llvm.x86.sse2.cmp.pd" => { + "llvm.x86.sse.cmp.ps" | "llvm.x86.sse2.cmp.pd" => { let (x, y, kind) = match args { [x, y, kind] => (x, y, kind), _ => bug!("wrong number of args for intrinsic {intrinsic}"), @@ -66,18 +80,95 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let flt_cc = match kind .try_to_bits(Size::from_bytes(1)) .unwrap_or_else(|| panic!("kind not scalar: {:?}", kind)) + .try_into() + .unwrap() { - 0 => FloatCC::Equal, - 1 => FloatCC::LessThan, - 2 => FloatCC::LessThanOrEqual, - 7 => FloatCC::Ordered, - 3 => FloatCC::Unordered, - 4 => FloatCC::NotEqual, - 5 => FloatCC::UnorderedOrGreaterThanOrEqual, - 6 => FloatCC::UnorderedOrGreaterThan, + _CMP_EQ_OQ | _CMP_EQ_OS => FloatCC::Equal, + _CMP_LT_OS | _CMP_LT_OQ => FloatCC::LessThan, + _CMP_LE_OS | _CMP_LE_OQ => FloatCC::LessThanOrEqual, + _CMP_UNORD_Q | _CMP_UNORD_S => FloatCC::Unordered, + _CMP_NEQ_UQ | _CMP_NEQ_US => FloatCC::NotEqual, + _CMP_NLT_US | _CMP_NLT_UQ => FloatCC::UnorderedOrGreaterThanOrEqual, + _CMP_NLE_US | _CMP_NLE_UQ => FloatCC::UnorderedOrGreaterThan, + _CMP_ORD_Q | _CMP_ORD_S => FloatCC::Ordered, + _CMP_EQ_UQ | _CMP_EQ_US => FloatCC::UnorderedOrEqual, + _CMP_NGE_US | _CMP_NGE_UQ => FloatCC::UnorderedOrLessThan, + _CMP_NGT_US | _CMP_NGT_UQ => FloatCC::UnorderedOrLessThanOrEqual, + _CMP_FALSE_OQ | _CMP_FALSE_OS => todo!(), + _CMP_NEQ_OQ | _CMP_NEQ_OS => FloatCC::OrderedNotEqual, + _CMP_GE_OS | _CMP_GE_OQ => FloatCC::GreaterThanOrEqual, + _CMP_GT_OS | _CMP_GT_OQ => FloatCC::GreaterThan, + _CMP_TRUE_UQ | _CMP_TRUE_US => todo!(), + kind => unreachable!("kind {:?}", kind), }; + // Copied from stdarch + /// Equal (ordered, non-signaling) + const _CMP_EQ_OQ: i32 = 0x00; + /// Less-than (ordered, signaling) + const _CMP_LT_OS: i32 = 0x01; + /// Less-than-or-equal (ordered, signaling) + const _CMP_LE_OS: i32 = 0x02; + /// Unordered (non-signaling) + const _CMP_UNORD_Q: i32 = 0x03; + /// Not-equal (unordered, non-signaling) + const _CMP_NEQ_UQ: i32 = 0x04; + /// Not-less-than (unordered, signaling) + const _CMP_NLT_US: i32 = 0x05; + /// Not-less-than-or-equal (unordered, signaling) + const _CMP_NLE_US: i32 = 0x06; + /// Ordered (non-signaling) + const _CMP_ORD_Q: i32 = 0x07; + /// Equal (unordered, non-signaling) + const _CMP_EQ_UQ: i32 = 0x08; + /// Not-greater-than-or-equal (unordered, signaling) + const _CMP_NGE_US: i32 = 0x09; + /// Not-greater-than (unordered, signaling) + const _CMP_NGT_US: i32 = 0x0a; + /// False (ordered, non-signaling) + const _CMP_FALSE_OQ: i32 = 0x0b; + /// Not-equal (ordered, non-signaling) + const _CMP_NEQ_OQ: i32 = 0x0c; + /// Greater-than-or-equal (ordered, signaling) + const _CMP_GE_OS: i32 = 0x0d; + /// Greater-than (ordered, signaling) + const _CMP_GT_OS: i32 = 0x0e; + /// True (unordered, non-signaling) + const _CMP_TRUE_UQ: i32 = 0x0f; + /// Equal (ordered, signaling) + const _CMP_EQ_OS: i32 = 0x10; + /// Less-than (ordered, non-signaling) + const _CMP_LT_OQ: i32 = 0x11; + /// Less-than-or-equal (ordered, non-signaling) + const _CMP_LE_OQ: i32 = 0x12; + /// Unordered (signaling) + const _CMP_UNORD_S: i32 = 0x13; + /// Not-equal (unordered, signaling) + const _CMP_NEQ_US: i32 = 0x14; + /// Not-less-than (unordered, non-signaling) + const _CMP_NLT_UQ: i32 = 0x15; + /// Not-less-than-or-equal (unordered, non-signaling) + const _CMP_NLE_UQ: i32 = 0x16; + /// Ordered (signaling) + const _CMP_ORD_S: i32 = 0x17; + /// Equal (unordered, signaling) + const _CMP_EQ_US: i32 = 0x18; + /// Not-greater-than-or-equal (unordered, non-signaling) + const _CMP_NGE_UQ: i32 = 0x19; + /// Not-greater-than (unordered, non-signaling) + const _CMP_NGT_UQ: i32 = 0x1a; + /// False (ordered, signaling) + const _CMP_FALSE_OS: i32 = 0x1b; + /// Not-equal (ordered, signaling) + const _CMP_NEQ_OS: i32 = 0x1c; + /// Greater-than-or-equal (ordered, non-signaling) + const _CMP_GE_OQ: i32 = 0x1d; + /// Greater-than (ordered, non-signaling) + const _CMP_GT_OQ: i32 = 0x1e; + /// True (unordered, signaling) + const _CMP_TRUE_US: i32 = 0x1f; + simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| { let res_lane = match lane_ty.kind() { ty::Float(_) => fx.bcx.ins().fcmp(flt_cc, x_lane, y_lane), @@ -103,6 +194,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( _ => fx.bcx.ins().iconst(types::I32, 0), }); } + "llvm.x86.sse2.psrai.d" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.sse2.psrai.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }); + } "llvm.x86.sse2.pslli.d" => { let (a, imm8) = match args { [a, imm8] => (a, imm8), @@ -137,6 +245,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( _ => fx.bcx.ins().iconst(types::I32, 0), }); } + "llvm.x86.sse2.psrai.w" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.sse2.psrai.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }); + } "llvm.x86.sse2.pslli.w" => { let (a, imm8) = match args { [a, imm8] => (a, imm8), @@ -171,6 +296,57 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( _ => fx.bcx.ins().iconst(types::I32, 0), }); } + "llvm.x86.avx.psrai.d" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.avx.psrai.d imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 32 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }); + } + "llvm.x86.sse2.psrli.q" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.avx.psrli.q imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 64 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }); + } + "llvm.x86.sse2.pslli.q" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.avx.pslli.q imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 64 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }); + } "llvm.x86.avx.pslli.d" => { let (a, imm8) = match args { [a, imm8] => (a, imm8), @@ -205,6 +381,23 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( _ => fx.bcx.ins().iconst(types::I32, 0), }); } + "llvm.x86.avx2.psrai.w" => { + let (a, imm8) = match args { + [a, imm8] => (a, imm8), + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8) + .expect("llvm.x86.avx.psrai.w imm8 not const"); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8 + .try_to_bits(Size::from_bytes(4)) + .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) + { + imm8 if imm8 < 16 => fx.bcx.ins().sshr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }); + } "llvm.x86.avx2.pslli.w" => { let (a, imm8) = match args { [a, imm8] => (a, imm8), @@ -313,7 +506,7 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( ret.place_lane(fx, 2).to_ptr().store(fx, res_2, MemFlags::trusted()); ret.place_lane(fx, 3).to_ptr().store(fx, res_3, MemFlags::trusted()); } - "llvm.x86.sse2.storeu.dq" => { + "llvm.x86.sse2.storeu.dq" | "llvm.x86.sse2.storeu.pd" => { intrinsic_args!(fx, args => (mem_addr, a); intrinsic); let mem_addr = mem_addr.load_scalar(fx); @@ -321,17 +514,45 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout()); dest.write_cvalue(fx, a); } - "llvm.x86.addcarry.64" => { + "llvm.x86.ssse3.pabs.b.128" | "llvm.x86.ssse3.pabs.w.128" | "llvm.x86.ssse3.pabs.d.128" => { + let a = match args { + [a] => a, + _ => bug!("wrong number of args for intrinsic {intrinsic}"), + }; + let a = codegen_operand(fx, a); + + simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| { + fx.bcx.ins().iabs(lane) + }); + } + "llvm.x86.addcarry.32" | "llvm.x86.addcarry.64" => { intrinsic_args!(fx, args => (c_in, a, b); intrinsic); let c_in = c_in.load_scalar(fx); - llvm_add_sub(fx, BinOp::Add, ret, c_in, a, b); + let (cb_out, c) = llvm_add_sub(fx, BinOp::Add, c_in, a, b); + + let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, a.layout().ty])); + let val = CValue::by_val_pair(cb_out, c, layout); + ret.write_cvalue(fx, val); } - "llvm.x86.subborrow.64" => { + "llvm.x86.addcarryx.u32" | "llvm.x86.addcarryx.u64" => { + intrinsic_args!(fx, args => (c_in, a, b, out); intrinsic); + let c_in = c_in.load_scalar(fx); + + let (cb_out, c) = llvm_add_sub(fx, BinOp::Add, c_in, a, b); + + Pointer::new(out.load_scalar(fx)).store(fx, c, MemFlags::trusted()); + ret.write_cvalue(fx, CValue::by_val(cb_out, fx.layout_of(fx.tcx.types.u8))); + } + "llvm.x86.subborrow.32" | "llvm.x86.subborrow.64" => { intrinsic_args!(fx, args => (b_in, a, b); intrinsic); let b_in = b_in.load_scalar(fx); - llvm_add_sub(fx, BinOp::Sub, ret, b_in, a, b); + let (cb_out, c) = llvm_add_sub(fx, BinOp::Sub, b_in, a, b); + + let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, a.layout().ty])); + let val = CValue::by_val_pair(cb_out, c, layout); + ret.write_cvalue(fx, val); } _ => { fx.tcx @@ -356,21 +577,11 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>( fn llvm_add_sub<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, - ret: CPlace<'tcx>, cb_in: Value, a: CValue<'tcx>, b: CValue<'tcx>, -) { - assert_eq!( - a.layout().ty, - fx.tcx.types.u64, - "llvm.x86.addcarry.64/llvm.x86.subborrow.64 second operand must be u64" - ); - assert_eq!( - b.layout().ty, - fx.tcx.types.u64, - "llvm.x86.addcarry.64/llvm.x86.subborrow.64 third operand must be u64" - ); +) -> (Value, Value) { + assert_eq!(a.layout().ty, b.layout().ty); // c + carry -> c + first intermediate carry or borrow respectively let int0 = crate::num::codegen_checked_int_binop(fx, bin_op, a, b); @@ -378,15 +589,14 @@ fn llvm_add_sub<'tcx>( let cb0 = int0.value_field(fx, FieldIdx::new(1)).load_scalar(fx); // c + carry -> c + second intermediate carry or borrow respectively - let cb_in_as_u64 = fx.bcx.ins().uextend(types::I64, cb_in); - let cb_in_as_u64 = CValue::by_val(cb_in_as_u64, fx.layout_of(fx.tcx.types.u64)); - let int1 = crate::num::codegen_checked_int_binop(fx, bin_op, c, cb_in_as_u64); + let clif_ty = fx.clif_type(a.layout().ty).unwrap(); + let cb_in_as_int = fx.bcx.ins().uextend(clif_ty, cb_in); + let cb_in_as_int = CValue::by_val(cb_in_as_int, fx.layout_of(a.layout().ty)); + let int1 = crate::num::codegen_checked_int_binop(fx, bin_op, c, cb_in_as_int); let (c, cb1) = int1.load_scalar_pair(fx); // carry0 | carry1 -> carry or borrow respectively let cb_out = fx.bcx.ins().bor(cb0, cb1); - let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, fx.tcx.types.u64])); - let val = CValue::by_val_pair(cb_out, c, layout); - ret.write_cvalue(fx, val); + (cb_out, c) } diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index da8ab361331..e3006b253b7 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -647,12 +647,13 @@ fn codegen_regular_intrinsic_call<'tcx>( let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout); ret.write_cvalue(fx, val); } - sym::volatile_store | sym::unaligned_volatile_store => { + sym::volatile_store | sym::unaligned_volatile_store | sym::nontemporal_store => { intrinsic_args!(fx, args => (ptr, val); intrinsic); let ptr = ptr.load_scalar(fx); // Cranelift treats stores as volatile by default // FIXME correctly handle unaligned_volatile_store + // FIXME actually do nontemporal stores if requested let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout()); dest.write_cvalue(fx, val); } diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 0de2dccda71..ebd153cb71d 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -268,8 +268,6 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Arc<dyn isa::Tar }; flags_builder.set("tls_model", tls_model).unwrap(); - flags_builder.set("enable_simd", "true").unwrap(); - flags_builder.set("enable_llvm_abi_extensions", "true").unwrap(); use rustc_session::config::OptLevel; diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index ec0b61a7ce5..ff95141ce90 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -2,6 +2,8 @@ use crate::prelude::*; +use rustc_middle::ty::FnSig; + use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::immediates::Offset32; @@ -160,6 +162,7 @@ impl<'tcx> CValue<'tcx> { } /// Load a value with layout.abi of scalar + #[track_caller] pub(crate) fn load_scalar(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> Value { let layout = self.1; match self.0 { @@ -182,6 +185,7 @@ impl<'tcx> CValue<'tcx> { } /// Load a value pair with layout.abi of scalar pair + #[track_caller] pub(crate) fn load_scalar_pair(self, fx: &mut FunctionCx<'_, '_, 'tcx>) -> (Value, Value) { let layout = self.1; match self.0 { @@ -583,17 +587,25 @@ impl<'tcx> CPlace<'tcx> { let dst_layout = self.layout(); match self.inner { CPlaceInner::Var(_local, var) => { - let data = CValue(from.0, dst_layout).load_scalar(fx); + let data = match from.1.abi { + Abi::Scalar(_) => CValue(from.0, dst_layout).load_scalar(fx), + _ => { + let (ptr, meta) = from.force_stack(fx); + assert!(meta.is_none()); + CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar(fx) + } + }; let dst_ty = fx.clif_type(self.layout().ty).unwrap(); transmute_scalar(fx, var, data, dst_ty); } CPlaceInner::VarPair(_local, var1, var2) => { - let (data1, data2) = if from.layout().ty == dst_layout.ty { - CValue(from.0, dst_layout).load_scalar_pair(fx) - } else { - let (ptr, meta) = from.force_stack(fx); - assert!(meta.is_none()); - CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx) + let (data1, data2) = match from.1.abi { + Abi::ScalarPair(_, _) => CValue(from.0, dst_layout).load_scalar_pair(fx), + _ => { + let (ptr, meta) = from.force_stack(fx); + assert!(meta.is_none()); + CValue(CValueInner::ByRef(ptr, None), dst_layout).load_scalar_pair(fx) + } }; let (dst_ty1, dst_ty2) = fx.clif_pair_type(self.layout().ty).unwrap(); transmute_scalar(fx, var1, data1, dst_ty1); @@ -607,30 +619,38 @@ impl<'tcx> CPlace<'tcx> { let mut flags = MemFlags::new(); flags.set_notrap(); - match from.layout().abi { - Abi::Scalar(_) => { - let val = from.load_scalar(fx); - to_ptr.store(fx, val, flags); - return; - } - Abi::ScalarPair(a_scalar, b_scalar) => { - let (value, extra) = from.load_scalar_pair(fx); - let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); - to_ptr.store(fx, value, flags); - to_ptr.offset(fx, b_offset).store(fx, extra, flags); - return; - } - _ => {} - } match from.0 { CValueInner::ByVal(val) => { to_ptr.store(fx, val, flags); } - CValueInner::ByValPair(_, _) => { - bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi); - } + CValueInner::ByValPair(val1, val2) => match from.layout().abi { + Abi::ScalarPair(a_scalar, b_scalar) => { + let b_offset = + scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + to_ptr.store(fx, val1, flags); + to_ptr.offset(fx, b_offset).store(fx, val2, flags); + } + _ => bug!("Non ScalarPair abi {:?} for ByValPair CValue", dst_layout.abi), + }, CValueInner::ByRef(from_ptr, None) => { + match from.layout().abi { + Abi::Scalar(_) => { + let val = from.load_scalar(fx); + to_ptr.store(fx, val, flags); + return; + } + Abi::ScalarPair(a_scalar, b_scalar) => { + let b_offset = + scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + let (val1, val2) = from.load_scalar_pair(fx); + to_ptr.store(fx, val1, flags); + to_ptr.offset(fx, b_offset).store(fx, val2, flags); + return; + } + _ => {} + } + let from_addr = from_ptr.get_addr(fx); let to_addr = to_ptr.get_addr(fx); let src_layout = from.1; @@ -815,11 +835,42 @@ pub(crate) fn assert_assignable<'tcx>( ParamEnv::reveal_all(), from_ty.fn_sig(fx.tcx), ); + let FnSig { + inputs_and_output: types_from, + c_variadic: c_variadic_from, + unsafety: unsafety_from, + abi: abi_from, + } = from_sig; let to_sig = fx .tcx .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_ty.fn_sig(fx.tcx)); + let FnSig { + inputs_and_output: types_to, + c_variadic: c_variadic_to, + unsafety: unsafety_to, + abi: abi_to, + } = to_sig; + let mut types_from = types_from.iter(); + let mut types_to = types_to.iter(); + loop { + match (types_from.next(), types_to.next()) { + (Some(a), Some(b)) => assert_assignable(fx, a, b, limit - 1), + (None, None) => break, + (Some(_), None) | (None, Some(_)) => panic!("{:#?}/{:#?}", from_ty, to_ty), + } + } + assert_eq!( + c_variadic_from, c_variadic_to, + "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", + from_sig, to_sig, fx, + ); + assert_eq!( + unsafety_from, unsafety_to, + "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", + from_sig, to_sig, fx, + ); assert_eq!( - from_sig, to_sig, + abi_from, abi_to, "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", from_sig, to_sig, fx, ); diff --git a/compiler/rustc_codegen_gcc/example/alloc_system.rs b/compiler/rustc_codegen_gcc/example/alloc_system.rs index 046903fe5ac..e756b347e89 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_system.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_system.rs @@ -10,6 +10,7 @@ #[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "mips", + target_arch = "mips32r6", target_arch = "powerpc", target_arch = "powerpc64"))] const MIN_ALIGN: usize = 8; @@ -17,6 +18,7 @@ const MIN_ALIGN: usize = 8; target_arch = "aarch64", target_arch = "loongarch64", target_arch = "mips64", + target_arch = "mips64r6", target_arch = "s390x", target_arch = "sparc64"))] const MIN_ALIGN: usize = 16; diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 97bc8ef9d1b..2fd0daee3e7 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -1,63 +1,6 @@ codegen_gcc_invalid_minimum_alignment = invalid minimum global alignment: {$err} -codegen_gcc_invalid_monomorphization_basic_integer = - invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` - -codegen_gcc_invalid_monomorphization_expected_signed_unsigned = - invalid monomorphization of `{$name}` intrinsic: expected element type `{$elem_ty}` of vector type `{$vec_ty}` to be a signed or unsigned integer type - -codegen_gcc_invalid_monomorphization_expected_simd = - invalid monomorphization of `{$name}` intrinsic: expected SIMD {$expected_ty} type, found non-SIMD `{$found_ty}` - -codegen_gcc_invalid_monomorphization_inserted_type = - invalid monomorphization of `{$name}` intrinsic: expected inserted type `{$in_elem}` (element of input `{$in_ty}`), found `{$out_ty}` - -codegen_gcc_invalid_monomorphization_invalid_bitmask = - invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` - -codegen_gcc_invalid_monomorphization_invalid_float_vector = - invalid monomorphization of `{$name}` intrinsic: unsupported element type `{$elem_ty}` of floating-point vector `{$vec_ty}` - -codegen_gcc_invalid_monomorphization_mask_type = - invalid monomorphization of `{$name}` intrinsic: mask element type is `{$ty}`, expected `i_` - -codegen_gcc_invalid_monomorphization_mismatched_lengths = - invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}` - -codegen_gcc_invalid_monomorphization_not_float = - invalid monomorphization of `{$name}` intrinsic: `{$ty}` is not a floating-point type - -codegen_gcc_invalid_monomorphization_return_element = - invalid monomorphization of `{$name}` intrinsic: expected return element type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` with element type `{$out_ty}` - -codegen_gcc_invalid_monomorphization_return_integer_type = - invalid monomorphization of `{$name}` intrinsic: expected return type with integer elements, found `{$ret_ty}` with non-integer `{$out_ty}` - -codegen_gcc_invalid_monomorphization_return_length = - invalid monomorphization of `{$name}` intrinsic: expected return type of length {$in_len}, found `{$ret_ty}` with length {$out_len} - -codegen_gcc_invalid_monomorphization_return_length_input_type = - invalid monomorphization of `{$name}` intrinsic: expected return type with length {$in_len} (same as input type `{$in_ty}`), found `{$ret_ty}` with length {$out_len} - -codegen_gcc_invalid_monomorphization_return_type = - invalid monomorphization of `{$name}` intrinsic: expected return type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` - -codegen_gcc_invalid_monomorphization_simd_shuffle = - invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}` - -codegen_gcc_invalid_monomorphization_unrecognized = - invalid monomorphization of `{$name}` intrinsic: unrecognized intrinsic `{$name}` - -codegen_gcc_invalid_monomorphization_unsupported_cast = - invalid monomorphization of `{$name}` intrinsic: unsupported cast from `{$in_ty}` with element `{$in_elem}` to `{$ret_ty}` with element `{$out_elem}` - -codegen_gcc_invalid_monomorphization_unsupported_element = - invalid monomorphization of `{$name}` intrinsic: unsupported {$name} from `{$in_ty}` with element `{$elem_ty}` to `{$ret_ty}` - -codegen_gcc_invalid_monomorphization_unsupported_operation = - invalid monomorphization of `{$name}` intrinsic: unsupported operation on `{$in_ty}` with element `{$in_elem}` - codegen_gcc_lto_not_supported = LTO is not supported. You may get a linker error. diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 43d0aafbd50..0b1f2fe6a87 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -27,7 +27,6 @@ use rustc_codegen_ssa::traits::{ BaseTypeMethods, BuilderMethods, ConstMethods, - DerivedTypeMethods, LayoutTypeMethods, HasCodegen, OverflowOp, diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index b62f4676f70..5f54cb16d8e 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -16,6 +16,10 @@ use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> { + self.context.new_cast(None, val, ty) + } + pub fn const_bytes(&self, bytes: &[u8]) -> RValue<'gcc> { bytes_in_context(self, bytes) } @@ -242,10 +246,6 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { const_alloc_to_gcc(self, alloc) } - fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> { - self.context.new_cast(None, val, ty) - } - fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { if value.get_type() == self.bool_type.make_pointer() { if let Some(pointee) = typ.get_pointee() { diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index 9305bd1e043..693367192b1 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -1,7 +1,6 @@ use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_macros::Diagnostic; -use rustc_middle::ty::Ty; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use std::borrow::Cow; struct ExitCode(Option<i32>); @@ -17,201 +16,6 @@ impl IntoDiagnosticArg for ExitCode { } #[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_basic_integer, code = "E0511")] -pub(crate) struct InvalidMonomorphizationBasicInteger<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_invalid_float_vector, code = "E0511")] -pub(crate) struct InvalidMonomorphizationInvalidFloatVector<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub elem_ty: &'a str, - pub vec_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_not_float, code = "E0511")] -pub(crate) struct InvalidMonomorphizationNotFloat<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_unrecognized, code = "E0511")] -pub(crate) struct InvalidMonomorphizationUnrecognized { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_expected_signed_unsigned, code = "E0511")] -pub(crate) struct InvalidMonomorphizationExpectedSignedUnsigned<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub elem_ty: Ty<'a>, - pub vec_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_unsupported_element, code = "E0511")] -pub(crate) struct InvalidMonomorphizationUnsupportedElement<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_ty: Ty<'a>, - pub elem_ty: Ty<'a>, - pub ret_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_invalid_bitmask, code = "E0511")] -pub(crate) struct InvalidMonomorphizationInvalidBitmask<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub ty: Ty<'a>, - pub expected_int_bits: u64, - pub expected_bytes: u64, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_simd_shuffle, code = "E0511")] -pub(crate) struct InvalidMonomorphizationSimdShuffle<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_expected_simd, code = "E0511")] -pub(crate) struct InvalidMonomorphizationExpectedSimd<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub position: &'a str, - pub found_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_mask_type, code = "E0511")] -pub(crate) struct InvalidMonomorphizationMaskType<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_return_length, code = "E0511")] -pub(crate) struct InvalidMonomorphizationReturnLength<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_len: u64, - pub ret_ty: Ty<'a>, - pub out_len: u64, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_return_length_input_type, code = "E0511")] -pub(crate) struct InvalidMonomorphizationReturnLengthInputType<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_len: u64, - pub in_ty: Ty<'a>, - pub ret_ty: Ty<'a>, - pub out_len: u64, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_return_element, code = "E0511")] -pub(crate) struct InvalidMonomorphizationReturnElement<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_elem: Ty<'a>, - pub in_ty: Ty<'a>, - pub ret_ty: Ty<'a>, - pub out_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_return_type, code = "E0511")] -pub(crate) struct InvalidMonomorphizationReturnType<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_elem: Ty<'a>, - pub in_ty: Ty<'a>, - pub ret_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_inserted_type, code = "E0511")] -pub(crate) struct InvalidMonomorphizationInsertedType<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_elem: Ty<'a>, - pub in_ty: Ty<'a>, - pub out_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_return_integer_type, code = "E0511")] -pub(crate) struct InvalidMonomorphizationReturnIntegerType<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub ret_ty: Ty<'a>, - pub out_ty: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_mismatched_lengths, code = "E0511")] -pub(crate) struct InvalidMonomorphizationMismatchedLengths { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub m_len: u64, - pub v_len: u64, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_unsupported_cast, code = "E0511")] -pub(crate) struct InvalidMonomorphizationUnsupportedCast<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_ty: Ty<'a>, - pub in_elem: Ty<'a>, - pub ret_ty: Ty<'a>, - pub out_elem: Ty<'a>, -} - -#[derive(Diagnostic)] -#[diag(codegen_gcc_invalid_monomorphization_unsupported_operation, code = "E0511")] -pub(crate) struct InvalidMonomorphizationUnsupportedOperation<'a> { - #[primary_span] - pub span: Span, - pub name: Symbol, - pub in_ty: Ty<'a>, - pub in_elem: Ty<'a>, -} - -#[derive(Diagnostic)] #[diag(codegen_gcc_lto_not_supported)] pub(crate) struct LTONotSupported; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index fe604be33b6..68edde13829 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -12,7 +12,8 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods}; #[cfg(feature="master")] -use rustc_codegen_ssa::traits::{DerivedTypeMethods, MiscMethods}; +use rustc_codegen_ssa::traits::MiscMethods; +use rustc_codegen_ssa::errors::InvalidMonomorphization; use rustc_middle::bug; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::ty::layout::LayoutOf; @@ -31,7 +32,6 @@ use crate::abi::FnAbiGccExt; use crate::builder::Builder; use crate::common::{SignType, TypeReflection}; use crate::context::CodegenCx; -use crate::errors::InvalidMonomorphizationBasicInteger; use crate::type_of::LayoutGccExt; use crate::intrinsic::simd::generic_simd_intrinsic; @@ -256,7 +256,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { _ => bug!(), }, None => { - tcx.sess.emit_err(InvalidMonomorphizationBasicInteger { span, name, ty }); + tcx.sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); return; } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 9115cf97119..f27de867d36 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -21,19 +21,6 @@ use rustc_target::abi::Align; use crate::builder::Builder; #[cfg(feature="master")] use crate::context::CodegenCx; -#[cfg(feature="master")] -use crate::errors::{InvalidMonomorphizationExpectedSignedUnsigned, InvalidMonomorphizationInsertedType}; -use crate::errors::{ - InvalidMonomorphizationExpectedSimd, - InvalidMonomorphizationInvalidBitmask, - InvalidMonomorphizationInvalidFloatVector, InvalidMonomorphizationMaskType, - InvalidMonomorphizationMismatchedLengths, InvalidMonomorphizationNotFloat, - InvalidMonomorphizationReturnElement, InvalidMonomorphizationReturnIntegerType, - InvalidMonomorphizationReturnLength, InvalidMonomorphizationReturnLengthInputType, - InvalidMonomorphizationReturnType, InvalidMonomorphizationSimdShuffle, - InvalidMonomorphizationUnrecognized, InvalidMonomorphizationUnsupportedElement, - InvalidMonomorphizationUnsupportedOperation, -}; pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( bx: &mut Builder<'a, 'gcc, 'tcx>, @@ -59,16 +46,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( }; } macro_rules! require_simd { - ($ty: expr, $position: expr) => { - require!( - $ty.is_simd(), - InvalidMonomorphizationExpectedSimd { - span, - name, - position: $position, - found_ty: $ty - } - ) + ($ty: expr, $diag: expr) => { + require!($ty.is_simd(), $diag) }; } @@ -78,7 +57,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let arg_tys = sig.inputs(); if name == sym::simd_select_bitmask { - require_simd!(arg_tys[1], "argument"); + require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] }); let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let expected_int_bits = (len.max(8) - 1).next_power_of_two(); @@ -99,10 +78,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty)); bx.load(int_ty, ptr, Align::ONE) } - _ => return_error!(InvalidMonomorphizationInvalidBitmask { + _ => return_error!(InvalidMonomorphization::InvalidBitmask { span, name, - ty: mask_ty, + mask_ty, expected_int_bits, expected_bytes }), @@ -131,7 +110,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } // every intrinsic below takes a SIMD vector as its first argument - require_simd!(arg_tys[0], "input"); + require_simd!(arg_tys[0], InvalidMonomorphization::SimdInput { span, name, ty: arg_tys[0] }); let in_ty = arg_tys[0]; let comparison = match name { @@ -146,12 +125,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx()); if let Some(cmp_op) = comparison { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, - InvalidMonomorphizationReturnLengthInputType { + InvalidMonomorphization::ReturnLengthInputType { span, name, in_len, @@ -162,7 +141,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ); require!( bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, - InvalidMonomorphizationReturnIntegerType { span, name, ret_ty, out_ty } + InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty } ); let arg1 = args[0].immediate(); @@ -190,7 +169,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( || span_bug!(span, "could not evaluate shuffle index array length"), ) } - _ => return_error!(InvalidMonomorphizationSimdShuffle { + _ => return_error!(InvalidMonomorphization::SimdShuffle { span, name, ty: args[2].layout.ty @@ -202,16 +181,16 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( }) }; - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); require!( out_len == n, - InvalidMonomorphizationReturnLength { span, name, in_len: n, ret_ty, out_len } + InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len } ); require!( in_elem == out_ty, - InvalidMonomorphizationReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty } + InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty } ); let vector = args[2].immediate(); @@ -223,7 +202,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_insert { require!( in_elem == arg_tys[2], - InvalidMonomorphizationInsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] } + InvalidMonomorphization::InsertedType { span, name, in_elem, in_ty, out_ty: arg_tys[2] } ); let vector = args[0].immediate(); let index = args[1].immediate(); @@ -240,7 +219,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_extract { require!( ret_ty == in_elem, - InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty } + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); let vector = args[0].immediate(); return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue()); @@ -249,26 +228,26 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::simd_select { let m_elem_ty = in_elem; let m_len = in_len; - require_simd!(arg_tys[1], "argument"); + require_simd!(arg_tys[1], InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] }); let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); require!( m_len == v_len, - InvalidMonomorphizationMismatchedLengths { span, name, m_len, v_len } + InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } ); match m_elem_ty.kind() { ty::Int(_) => {} - _ => return_error!(InvalidMonomorphizationMaskType { span, name, ty: m_elem_ty }), + _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }), } return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate())); } #[cfg(feature="master")] if name == sym::simd_cast || name == sym::simd_as { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, - InvalidMonomorphizationReturnLengthInputType { + InvalidMonomorphization::ReturnLengthInputType { span, name, in_len, @@ -329,7 +308,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( })* _ => {}, } - return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem }) + return_error!(InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }) })* } } @@ -422,12 +401,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( 32 => ("f", elem_ty), 64 => ("", elem_ty), _ => { - return_error!(InvalidMonomorphizationInvalidFloatVector { span, name, elem_ty: f.name_str(), vec_ty: in_ty }); + return_error!(InvalidMonomorphization::FloatingPointVector { span, name, f_ty: *f, in_ty }); } } } else { - return_error!(InvalidMonomorphizationNotFloat { span, name, ty: in_ty }); + return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty }); }; let vec_ty = bx.cx.type_vector(elem_ty, in_len); @@ -450,7 +429,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( sym::simd_fsqrt => "sqrt", sym::simd_round => "round", sym::simd_trunc => "trunc", - _ => return_error!(InvalidMonomorphizationUnrecognized { span, name }) + _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }) }; let builtin_name = format!("{}{}", intr_name, elem_ty_str); let funcs = bx.cx.functions.borrow(); @@ -566,10 +545,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // * M: any integer width is supported, will be truncated to i1 // All types must be simd vector types - require_simd!(in_ty, "first"); - require_simd!(arg_tys[1], "second"); - require_simd!(arg_tys[2], "third"); - require_simd!(ret_ty, "return"); + require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); + require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] }); + require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] }); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); // Of the same length: let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); @@ -674,9 +653,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // * M: any integer width is supported, will be truncated to i1 // All types must be simd vector types - require_simd!(in_ty, "first"); - require_simd!(arg_tys[1], "second"); - require_simd!(arg_tys[2], "third"); + require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); + require_simd!(arg_tys[1], InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] }); + require_simd!(arg_tys[2], InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] }); // Of the same length: let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); @@ -815,7 +794,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( })* _ => {}, } - return_error!(InvalidMonomorphizationUnsupportedOperation { span, name, in_ty, in_elem }) + return_error!(InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem }) })* } } @@ -835,11 +814,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_int_from_ty(i)), ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits) / 8, bx.cx.type_uint_from_ty(i)), _ => { - return_error!(InvalidMonomorphizationExpectedSignedUnsigned { + return_error!(InvalidMonomorphization::ExpectedVectorElementType { span, name, - elem_ty: arg_tys[0].simd_size_and_type(bx.tcx()).1, - vec_ty: arg_tys[0], + expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1, + vector_type: arg_tys[0], }); } }; @@ -925,7 +904,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::$name { require!( ret_ty == in_elem, - InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty } + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { @@ -947,11 +926,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( Ok(bx.vector_reduce_op(args[0].immediate(), $vec_op)) } } - _ => return_error!(InvalidMonomorphizationUnsupportedElement { + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, + symbol: sym::$name, in_ty, - elem_ty: in_elem, + in_elem, ret_ty }), }; @@ -994,12 +974,19 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( if name == sym::$name { require!( ret_ty == in_elem, - InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty } + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => Ok(bx.$int_red(args[0].immediate())), ty::Float(_) => Ok(bx.$float_red(args[0].immediate())), - _ => return_error!(InvalidMonomorphizationUnsupportedElement { span, name, in_ty, elem_ty: in_elem, ret_ty }), + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { + span, + name, + symbol: sym::$name, + in_ty, + in_elem, + ret_ty + }), }; } }; @@ -1017,17 +1004,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let input = if !$boolean { require!( ret_ty == in_elem, - InvalidMonomorphizationReturnType { span, name, in_elem, in_ty, ret_ty } + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); args[0].immediate() } else { match in_elem.kind() { ty::Int(_) | ty::Uint(_) => {} - _ => return_error!(InvalidMonomorphizationUnsupportedElement { + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, + symbol: sym::$name, in_ty, - elem_ty: in_elem, + in_elem, ret_ty }), } @@ -1039,11 +1027,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let r = bx.vector_reduce_op(input, $op); Ok(if !$boolean { r } else { bx.icmp(IntPredicate::IntNE, r, bx.context.new_rvalue_zero(r.get_type())) }) } - _ => return_error!(InvalidMonomorphizationUnsupportedElement { + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, + symbol: sym::$name, in_ty, - elem_ty: in_elem, + in_elem, ret_ty }), }; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 2a6b642782d..697ae015fed 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -71,7 +71,7 @@ use gccjit::{Context, OptimizationLevel, CType}; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; use rustc_codegen_ssa::base::codegen_crate; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryFn}; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use rustc_codegen_ssa::target_features::supported_target_features; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; @@ -217,14 +217,14 @@ impl WriteBackendMethods for GccCodegenBackend { type ThinData = (); type ThinBuffer = ThinBuffer; - fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLTOInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> { + fn run_fat_lto(_cgcx: &CodegenContext<Self>, mut modules: Vec<FatLtoInput<Self>>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<LtoModuleCodegen<Self>, FatalError> { // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins. // NOTE: implemented elsewhere. // TODO(antoyo): what is implemented elsewhere ^ ? let module = match modules.remove(0) { - FatLTOInput::InMemory(module) => module, - FatLTOInput::Serialized { .. } => { + FatLtoInput::InMemory(module) => module, + FatLtoInput::Serialized { .. } => { unimplemented!(); } }; @@ -239,6 +239,10 @@ impl WriteBackendMethods for GccCodegenBackend { unimplemented!(); } + fn print_statistics(&self) { + unimplemented!() + } + unsafe fn optimize(_cgcx: &CodegenContext<Self>, _diag_handler: &Handler, module: &ModuleCodegen<Self::Module>, config: &ModuleConfig) -> Result<(), FatalError> { module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); Ok(()) diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 521b64ad34d..31899740514 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -54,6 +54,23 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.u128_type } + pub fn type_ptr_to(&self, ty: Type<'gcc>) -> Type<'gcc> { + ty.make_pointer() + } + + pub fn type_ptr_to_ext(&self, ty: Type<'gcc>, _address_space: AddressSpace) -> Type<'gcc> { + // TODO(antoyo): use address_space, perhaps with TYPE_ADDR_SPACE? + ty.make_pointer() + } + + pub fn type_i8p(&self) -> Type<'gcc> { + self.type_ptr_to(self.type_i8()) + } + + pub fn type_i8p_ext(&self, address_space: AddressSpace) -> Type<'gcc> { + self.type_ptr_to_ext(self.type_i8(), address_space) + } + pub fn type_pointee_for_align(&self, align: Align) -> Type<'gcc> { // FIXME(eddyb) We could find a better approximation if ity.align < align. let ity = Integer::approximate_align(self, align); @@ -149,13 +166,12 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } } - fn type_ptr_to(&self, ty: Type<'gcc>) -> Type<'gcc> { - ty.make_pointer() + fn type_ptr(&self) -> Type<'gcc> { + self.type_ptr_to(self.type_void()) } - fn type_ptr_to_ext(&self, ty: Type<'gcc>, _address_space: AddressSpace) -> Type<'gcc> { - // TODO(antoyo): use address_space, perhaps with TYPE_ADDR_SPACE? - ty.make_pointer() + fn type_ptr_ext(&self, address_space: AddressSpace) -> Type<'gcc> { + self.type_ptr_to_ext(self.type_void(), address_space) } fn element_type(&self, ty: Type<'gcc>) -> Type<'gcc> { diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index de1622951fe..aed4a8f3c85 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -1,7 +1,8 @@ codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} codegen_llvm_dlltool_fail_import_library = - Dlltool could not create import library: {$stdout} + Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: + {$stdout} {$stderr} codegen_llvm_dynamic_linking_with_lto = diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index d221bad28ef..e02b457fd0b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -216,9 +216,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. let can_store_through_cast_ptr = false; if can_store_through_cast_ptr { - let cast_ptr_llty = bx.type_ptr_to(cast.llvm_type(bx)); - let cast_dst = bx.pointercast(dst.llval, cast_ptr_llty); - bx.store(val, cast_dst, self.layout.align.abi); + bx.store(val, dst.llval, self.layout.align.abi); } else { // The actual return type is a struct, but the ABI // adaptation code has cast it into some scalar type. The @@ -336,7 +334,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx), PassMode::Cast(cast, _) => cast.llvm_type(cx), PassMode::Indirect { .. } => { - llargument_tys.push(cx.type_ptr_to(self.ret.memory_ty(cx))); + llargument_tys.push(cx.type_ptr()); cx.type_void() } }; @@ -364,9 +362,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } cast.llvm_type(cx) } - PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { - cx.type_ptr_to(arg.memory_ty(cx)) - } + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => cx.type_ptr(), }; llargument_tys.push(llarg_ty); } @@ -379,12 +375,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { } fn ptr_to_llvm_type(&self, cx: &CodegenCx<'ll, 'tcx>) -> &'ll Type { - unsafe { - llvm::LLVMPointerType( - self.llvm_type(cx), - cx.data_layout().instruction_address_space.0 as c_uint, - ) - } + cx.type_ptr_ext(cx.data_layout().instruction_address_space) } fn llvm_cconv(&self) -> llvm::CallConv { diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index a57508815d6..ca123334fca 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -28,7 +28,7 @@ pub(crate) unsafe fn codegen( tws => bug!("Unsupported target word size for int: {}", tws), }; let i8 = llvm::LLVMInt8TypeInContext(llcx); - let i8p = llvm::LLVMPointerType(i8, 0); + let i8p = llvm::LLVMPointerTypeInContext(llcx, 0); let void = llvm::LLVMVoidTypeInContext(llcx); if kind == AllocatorKind::Default { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 39275272e42..4c9094bf1f5 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -335,6 +335,10 @@ pub fn from_fn_attrs<'ll, 'tcx>( to_add.extend(probestack_attr(cx)); to_add.extend(stackprotector_attr(cx)); + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) { + to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins")); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { to_add.push(AttributeKind::Cold.create_attr(cx.llcx)); } @@ -359,50 +363,44 @@ pub fn from_fn_attrs<'ll, 'tcx>( if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) { - if llvm_util::get_version() >= (15, 0, 0) { - to_add.push(create_alloc_family_attr(cx.llcx)); - // apply to argument place instead of function - let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); - attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); - to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0)); - let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned; - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { - flags |= AllocKindFlags::Uninitialized; - } else { - flags |= AllocKindFlags::Zeroed; - } - to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags)); + to_add.push(create_alloc_family_attr(cx.llcx)); + // apply to argument place instead of function + let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(1), &[alloc_align]); + to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 0)); + let mut flags = AllocKindFlags::Alloc | AllocKindFlags::Aligned; + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + flags |= AllocKindFlags::Uninitialized; + } else { + flags |= AllocKindFlags::Zeroed; } + to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags)); // apply to return place instead of function (unlike all other attributes applied in this function) let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::REALLOCATOR) { - if llvm_util::get_version() >= (15, 0, 0) { - to_add.push(create_alloc_family_attr(cx.llcx)); - to_add.push(llvm::CreateAllocKindAttr( - cx.llcx, - AllocKindFlags::Realloc | AllocKindFlags::Aligned, - )); - // applies to argument place instead of function place - let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); - attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); - // apply to argument place instead of function - let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); - attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]); - to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3)); - } + to_add.push(create_alloc_family_attr(cx.llcx)); + to_add.push(llvm::CreateAllocKindAttr( + cx.llcx, + AllocKindFlags::Realloc | AllocKindFlags::Aligned, + )); + // applies to argument place instead of function place + let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); + // apply to argument place instead of function + let alloc_align = AttributeKind::AllocAlign.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(2), &[alloc_align]); + to_add.push(llvm::CreateAllocSizeAttr(cx.llcx, 3)); let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::DEALLOCATOR) { - if llvm_util::get_version() >= (15, 0, 0) { - to_add.push(create_alloc_family_attr(cx.llcx)); - to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free)); - // applies to argument place instead of function place - let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); - attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); - } + to_add.push(create_alloc_family_attr(cx.llcx)); + to_add.push(llvm::CreateAllocKindAttr(cx.llcx, AllocKindFlags::Free)); + // applies to argument place instead of function place + let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx); + attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]); } if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY) { to_add.push(llvm::CreateAttrString(cx.llcx, "cmse_nonsecure_entry")); @@ -446,7 +444,7 @@ pub fn from_fn_attrs<'ll, 'tcx>( let mut function_features = function_features .iter() .flat_map(|feat| { - llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{}", f)) + llvm_util::to_llvm_features(cx.tcx.sess, feat).into_iter().map(|f| format!("+{f}")) }) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index a6416e9540c..a82d2c5771a 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -56,7 +56,7 @@ fn llvm_machine_type(cpu: &str) -> LLVMMachineType { "x86" => LLVMMachineType::I386, "aarch64" => LLVMMachineType::ARM64, "arm" => LLVMMachineType::ARM, - _ => panic!("unsupported cpu type {}", cpu), + _ => panic!("unsupported cpu type {cpu}"), } } @@ -128,7 +128,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { let name_suffix = if is_direct_dependency { "_imports" } else { "_imports_indirect" }; let output_path = { let mut output_path: PathBuf = tmpdir.to_path_buf(); - output_path.push(format!("{}{}", lib_name, name_suffix)); + output_path.push(format!("{lib_name}{name_suffix}")); output_path.with_extension("lib") }; @@ -156,7 +156,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { // functions. Therefore, use binutils to create the import library instead, // by writing a .DEF file to the temp dir and calling binutils's dlltool. let def_file_path = - tmpdir.join(format!("{}{}", lib_name, name_suffix)).with_extension("def"); + tmpdir.join(format!("{lib_name}{name_suffix}")).with_extension("def"); let def_file_content = format!( "EXPORTS\n{}", @@ -164,7 +164,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { .into_iter() .map(|(name, ordinal)| { match ordinal { - Some(n) => format!("{} @{} NONAME", name, n), + Some(n) => format!("{name} @{n} NONAME"), None => name, } }) @@ -198,25 +198,24 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { "arm" => ("arm", "--32"), _ => panic!("unsupported arch {}", sess.target.arch), }; - let result = std::process::Command::new(&dlltool) - .args([ - "-d", - def_file_path.to_str().unwrap(), - "-D", - lib_name, - "-l", - output_path.to_str().unwrap(), - "-m", - dlltool_target_arch, - "-f", - dlltool_target_bitness, - "--no-leading-underscore", - "--temp-prefix", - temp_prefix.to_str().unwrap(), - ]) - .output(); - - match result { + let mut dlltool_cmd = std::process::Command::new(&dlltool); + dlltool_cmd.args([ + "-d", + def_file_path.to_str().unwrap(), + "-D", + lib_name, + "-l", + output_path.to_str().unwrap(), + "-m", + dlltool_target_arch, + "-f", + dlltool_target_bitness, + "--no-leading-underscore", + "--temp-prefix", + temp_prefix.to_str().unwrap(), + ]); + + match dlltool_cmd.output() { Err(e) => { sess.emit_fatal(ErrorCallingDllTool { dlltool_path: dlltool.to_string_lossy(), @@ -226,6 +225,12 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder { // dlltool returns '0' on failure, so check for error output instead. Ok(output) if !output.stderr.is_empty() => { sess.emit_fatal(DlltoolFailImportLibrary { + dlltool_path: dlltool.to_string_lossy(), + dlltool_args: dlltool_cmd + .get_args() + .map(|arg| arg.to_string_lossy()) + .collect::<Vec<_>>() + .join(" "), stdout: String::from_utf8_lossy(&output.stdout), stderr: String::from_utf8_lossy(&output.stderr), }) @@ -430,7 +435,7 @@ impl<'a> LlvmArchiveBuilder<'a> { } fn string_to_io_error(s: String) -> io::Error { - io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) + io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}")) } fn find_binutils_dlltool(sess: &Session) -> OsString { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index d7dd98d7938..b2d28cef899 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,7 +7,7 @@ use crate::{LlvmCodegenBackend, ModuleLlvm}; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; -use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig}; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, TargetMachineFactoryConfig}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; @@ -166,7 +166,7 @@ fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], LtoBitcodeFro /// for further optimization. pub(crate) fn run_fat( cgcx: &CodegenContext<LlvmCodegenBackend>, - modules: Vec<FatLTOInput<LlvmCodegenBackend>>, + modules: Vec<FatLtoInput<LlvmCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, ) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> { let diag_handler = cgcx.create_diag_handler(); @@ -220,7 +220,7 @@ pub(crate) fn prepare_thin(module: ModuleCodegen<ModuleLlvm>) -> (String, ThinBu fn fat_lto( cgcx: &CodegenContext<LlvmCodegenBackend>, diag_handler: &Handler, - modules: Vec<FatLTOInput<LlvmCodegenBackend>>, + modules: Vec<FatLtoInput<LlvmCodegenBackend>>, cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>, mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>, symbols_below_threshold: &[*const libc::c_char], @@ -245,8 +245,8 @@ fn fat_lto( })); for module in modules { match module { - FatLTOInput::InMemory(m) => in_memory.push(m), - FatLTOInput::Serialized { name, buffer } => { + FatLtoInput::InMemory(m) => in_memory.push(m), + FatLtoInput::Serialized { name, buffer } => { info!("pushing serialized module {:?}", name); let buffer = SerializedModule::Local(buffer); serialized_modules.push((buffer, CString::new(name).unwrap())); @@ -332,7 +332,7 @@ fn fat_lto( let _timer = cgcx .prof .generic_activity_with_arg_recorder("LLVM_fat_lto_link_module", |recorder| { - recorder.record_arg(format!("{:?}", name)) + recorder.record_arg(format!("{name:?}")) }); info!("linking {:?}", name); let data = bc_decoded.data(); @@ -787,7 +787,7 @@ impl ThinLTOKeysMap { let file = File::create(path)?; let mut writer = io::BufWriter::new(file); for (module, key) in &self.keys { - writeln!(writer, "{} {}", module, key)?; + writeln!(writer, "{module} {key}")?; } Ok(()) } @@ -801,7 +801,7 @@ impl ThinLTOKeysMap { let mut split = line.split(' '); let module = split.next().unwrap(); let key = split.next().unwrap(); - assert_eq!(split.next(), None, "Expected two space-separated values, found {:?}", line); + assert_eq!(split.next(), None, "Expected two space-separated values, found {line:?}"); keys.insert(module.to_string(), key.to_string()); } Ok(Self { keys }) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 46874b50990..46e6daed21f 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -4,7 +4,6 @@ use crate::back::profiling::{ }; use crate::base; use crate::common; -use crate::consts; use crate::errors::{ CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, WithLlvmError, WriteBytecode, }; @@ -259,7 +258,7 @@ pub(crate) fn save_temp_bitcode( return; } unsafe { - let ext = format!("{}.bc", name); + let ext = format!("{name}.bc"); let cgu = Some(&module.name[..]); let path = cgcx.output_filenames.temp_path_ext(&ext, cgu); let cstr = path_to_c_string(&path); @@ -706,7 +705,7 @@ pub(crate) unsafe fn codegen( let Ok(demangled) = rustc_demangle::try_demangle(input) else { return 0 }; - if write!(cursor, "{:#}", demangled).is_err() { + if write!(cursor, "{demangled:#}").is_err() { // Possible only if provided buffer is not big enough return 0; } @@ -827,7 +826,7 @@ pub(crate) unsafe fn codegen( } fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data: &[u8]) -> Vec<u8> { - let mut asm = format!(".section {},\"{}\"\n", section_name, section_flags).into_bytes(); + let mut asm = format!(".section {section_name},\"{section_flags}\"\n").into_bytes(); asm.extend_from_slice(b".ascii \""); asm.reserve(data.len()); for &byte in data { @@ -985,7 +984,7 @@ fn create_msvc_imps( let prefix = if cgcx.target_arch == "x86" { "\x01__imp__" } else { "\x01__imp_" }; unsafe { - let i8p_ty = Type::i8p_llcx(llcx); + let ptr_ty = Type::ptr_llcx(llcx); let globals = base::iter_globals(llmod) .filter(|&val| { llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage @@ -1005,8 +1004,8 @@ fn create_msvc_imps( .collect::<Vec<_>>(); for (imp_name, val) in globals { - let imp = llvm::LLVMAddGlobal(llmod, i8p_ty, imp_name.as_ptr().cast()); - llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); + let imp = llvm::LLVMAddGlobal(llmod, ptr_ty, imp_name.as_ptr().cast()); + llvm::LLVMSetInitializer(imp, val); llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); } } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 5b5f81c0329..b659fd02eec 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -123,8 +123,7 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen // happen after the llvm.used variables are created. for &(old_g, new_g) in cx.statics_to_rauw().borrow().iter() { unsafe { - let bitcast = llvm::LLVMConstPointerCast(new_g, cx.val_ty(old_g)); - llvm::LLVMReplaceAllUsesWith(old_g, bitcast); + llvm::LLVMReplaceAllUsesWith(old_g, new_g); llvm::LLVMDeleteGlobal(old_g); } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index d55992bf092..5408481df48 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -652,7 +652,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { flags: MemFlags, ) -> &'ll Value { debug!("Store {:?} -> {:?} ({:?})", val, ptr, flags); - let ptr = self.check_store(val, ptr); + assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); let align = @@ -682,7 +682,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { size: Size, ) { debug!("Store {:?} -> {:?}", val, ptr); - let ptr = self.check_store(val, ptr); + assert_eq!(self.cx.type_kind(self.cx.val_ty(ptr)), TypeKind::Pointer); unsafe { let store = llvm::LLVMRustBuildAtomicStore( self.llbuilder, @@ -873,8 +873,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_isize(), false); let is_volatile = flags.contains(MemFlags::VOLATILE); - let dst = self.pointercast(dst, self.type_i8p()); - let src = self.pointercast(src, self.type_i8p()); unsafe { llvm::LLVMRustBuildMemCpy( self.llbuilder, @@ -900,8 +898,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memmove not supported"); let size = self.intcast(size, self.type_isize(), false); let is_volatile = flags.contains(MemFlags::VOLATILE); - let dst = self.pointercast(dst, self.type_i8p()); - let src = self.pointercast(src, self.type_i8p()); unsafe { llvm::LLVMRustBuildMemMove( self.llbuilder, @@ -924,7 +920,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { flags: MemFlags, ) { let is_volatile = flags.contains(MemFlags::VOLATILE); - let ptr = self.pointercast(ptr, self.type_i8p()); unsafe { llvm::LLVMRustBuildMemSet( self.llbuilder, @@ -981,7 +976,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn cleanup_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) { - let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); + let ty = self.type_struct(&[self.type_ptr(), self.type_i32()], false); let landing_pad = self.landing_pad(ty, pers_fn, 0); unsafe { llvm::LLVMSetCleanup(landing_pad, llvm::True); @@ -990,14 +985,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn filter_landing_pad(&mut self, pers_fn: &'ll Value) -> (&'ll Value, &'ll Value) { - let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); + let ty = self.type_struct(&[self.type_ptr(), self.type_i32()], false); let landing_pad = self.landing_pad(ty, pers_fn, 1); - self.add_clause(landing_pad, self.const_array(self.type_i8p(), &[])); + self.add_clause(landing_pad, self.const_array(self.type_ptr(), &[])); (self.extract_value(landing_pad, 0), self.extract_value(landing_pad, 1)) } fn resume(&mut self, exn0: &'ll Value, exn1: &'ll Value) { - let ty = self.type_struct(&[self.type_i8p(), self.type_i32()], false); + let ty = self.type_struct(&[self.type_ptr(), self.type_i32()], false); let mut exn = self.const_poison(ty); exn = self.insert_value(exn, exn0, 0); exn = self.insert_value(exn, exn1, 1); @@ -1161,7 +1156,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let llfn = unsafe { llvm::LLVMRustGetInstrProfIncrementIntrinsic(self.cx().llmod) }; let llty = self.cx.type_func( - &[self.cx.type_i8p(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_i32()], + &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32(), self.cx.type_i32()], self.cx.type_void(), ); let args = &[fn_name, hash, num_counters, index]; @@ -1387,25 +1382,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { ret.expect("LLVM does not have support for catchret") } - fn check_store(&mut self, val: &'ll Value, ptr: &'ll Value) -> &'ll Value { - let dest_ptr_ty = self.cx.val_ty(ptr); - let stored_ty = self.cx.val_ty(val); - let stored_ptr_ty = self.cx.type_ptr_to(stored_ty); - - assert_eq!(self.cx.type_kind(dest_ptr_ty), TypeKind::Pointer); - - if dest_ptr_ty == stored_ptr_ty { - ptr - } else { - debug!( - "type mismatch in store. \ - Expected {:?}, got {:?}; inserting bitcast", - dest_ptr_ty, stored_ptr_ty - ); - self.bitcast(ptr, stored_ptr_ty) - } - } - fn check_call<'b>( &mut self, typ: &str, @@ -1415,9 +1391,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { ) -> Cow<'b, [&'ll Value]> { assert!( self.cx.type_kind(fn_ty) == TypeKind::Function, - "builder::{} not passed a function, but {:?}", - typ, - fn_ty + "builder::{typ} not passed a function, but {fn_ty:?}" ); let param_tys = self.cx.func_params_types(fn_ty); @@ -1468,7 +1442,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - let ptr = self.pointercast(ptr, self.cx.type_i8p()); self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]); } @@ -1509,12 +1482,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let instr = if signed { "fptosi" } else { "fptoui" }; let name = if let Some(vector_length) = vector_length { - format!( - "llvm.{}.sat.v{}i{}.v{}f{}", - instr, vector_length, int_width, vector_length, float_width - ) + format!("llvm.{instr}.sat.v{vector_length}i{int_width}.v{vector_length}f{float_width}") } else { - format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width) + format!("llvm.{instr}.sat.i{int_width}.f{float_width}") }; let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None) diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 5d3e245f45f..36c098218cf 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -4,13 +4,11 @@ //! and methods are represented as just a fn ptr and not a full //! closure. -use crate::abi::FnAbiLlvmExt; use crate::attributes; use crate::common; use crate::context::CodegenCx; use crate::llvm; use crate::value::Value; -use rustc_codegen_ssa::traits::*; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; @@ -45,39 +43,7 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); let llfn = if let Some(llfn) = cx.get_declared_value(sym) { - // Create a fn pointer with the new signature. - let llptrty = fn_abi.ptr_to_llvm_type(cx); - - // This is subtle and surprising, but sometimes we have to bitcast - // the resulting fn pointer. The reason has to do with external - // functions. If you have two crates that both bind the same C - // library, they may not use precisely the same types: for - // example, they will probably each declare their own structs, - // which are distinct types from LLVM's point of view (nominal - // types). - // - // Now, if those two crates are linked into an application, and - // they contain inlined code, you can wind up with a situation - // where both of those functions wind up being loaded into this - // application simultaneously. In that case, the same function - // (from LLVM's point of view) requires two types. But of course - // LLVM won't allow one function to have two types. - // - // What we currently do, therefore, is declare the function with - // one of the two types (whichever happens to come first) and then - // bitcast as needed when the function is referenced to make sure - // it has the type we expect. - // - // This can occur on either a crate-local or crate-external - // reference. It also occurs when testing libcore and in some - // other weird situations. Annoying. - if cx.val_ty(llfn) != llptrty { - debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); - cx.const_ptrcast(llfn, llptrty) - } else { - debug!("get_fn: not casting pointer!"); - llfn - } + llfn } else { let instance_def_id = instance.def_id(); let llfn = if tcx.sess.target.arch == "x86" && diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 4d879fbdcb7..0b0816c27b6 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -1,10 +1,9 @@ //! Code that is useful in various codegen modules. -use crate::consts::{self, const_alloc_to_llvm}; +use crate::consts::const_alloc_to_llvm; pub use crate::context::CodegenCx; use crate::llvm::{self, BasicBlock, Bool, ConstantInt, False, OperandBundleDef, True}; use crate::type_::Type; -use crate::type_of::LayoutLlvmExt; use crate::value::Value; use rustc_ast::Mutability; @@ -13,7 +12,6 @@ use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType}; use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer}; @@ -211,11 +209,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { }) .1; let len = s.len(); - let cs = consts::ptrcast( - str_global, - self.type_ptr_to(self.layout_of(self.tcx.types.str_).llvm_type(self)), - ); - (cs, self.const_usize(len as u64)) + (str_global, self.const_usize(len as u64)) } fn const_struct(&self, elts: &[&'ll Value], packed: bool) -> &'ll Value { @@ -292,7 +286,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { let llval = unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), - self.const_bitcast(base_addr, self.type_i8p_ext(base_addr_space)), + self.const_bitcast(base_addr, self.type_ptr_ext(base_addr_space)), &self.const_usize(offset.bytes()), 1, ) @@ -310,10 +304,6 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { const_alloc_to_llvm(self, alloc) } - fn const_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { - consts::ptrcast(val, ty) - } - fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { self.const_bitcast(val, ty) } @@ -322,7 +312,7 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMConstInBoundsGEP2( self.type_i8(), - self.const_bitcast(base_addr, self.type_i8p()), + base_addr, &self.const_usize(offset.bytes()), 1, ) @@ -420,10 +410,10 @@ pub(crate) fn i686_decorated_name( DllCallingConvention::C => {} DllCallingConvention::Stdcall(arg_list_size) | DllCallingConvention::Fastcall(arg_list_size) => { - write!(&mut decorated_name, "@{}", arg_list_size).unwrap(); + write!(&mut decorated_name, "@{arg_list_size}").unwrap(); } DllCallingConvention::Vectorcall(arg_list_size) => { - write!(&mut decorated_name, "@@{}", arg_list_size).unwrap(); + write!(&mut decorated_name, "@@{arg_list_size}").unwrap(); } } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index df52f50f86f..95af2f8ef4a 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -103,7 +103,7 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation< value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(dl.pointer_size), }, - cx.type_i8p_ext(address_space), + cx.type_ptr_ext(address_space), )); next_offset = offset + pointer_size; } @@ -179,7 +179,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( }) }); llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); - llvm::LLVMSetInitializer(g2, cx.const_ptrcast(g1, llty)); + llvm::LLVMSetInitializer(g2, g1); g2 } } else if cx.tcx.sess.target.arch == "x86" && @@ -193,10 +193,6 @@ fn check_and_apply_linkage<'ll, 'tcx>( } } -pub fn ptrcast<'ll>(val: &'ll Value, ty: &'ll Type) -> &'ll Value { - unsafe { llvm::LLVMConstPointerCast(val, ty) } -} - impl<'ll> CodegenCx<'ll, '_> { pub(crate) fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMConstBitCast(val, ty) } @@ -238,8 +234,7 @@ impl<'ll> CodegenCx<'ll, '_> { assert!( !defined_in_current_codegen_unit, "consts::get_static() should always hit the cache for \ - statics defined in the same CGU, but did not for `{:?}`", - def_id + statics defined in the same CGU, but did not for `{def_id:?}`" ); let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); @@ -251,7 +246,7 @@ impl<'ll> CodegenCx<'ll, '_> { let g = if def_id.is_local() && !self.tcx.is_foreign_item(def_id) { let llty = self.layout_of(ty).llvm_type(self); if let Some(g) = self.get_declared_value(sym) { - if self.val_ty(g) != self.type_ptr_to(llty) { + if self.val_ty(g) != self.type_ptr() { span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } } @@ -552,16 +547,14 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { } } - /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. + /// Add a global value to a list to be stored in the `llvm.used` variable, an array of ptr. fn add_used_global(&self, global: &'ll Value) { - let cast = unsafe { llvm::LLVMConstPointerCast(global, self.type_i8p()) }; - self.used_statics.borrow_mut().push(cast); + self.used_statics.borrow_mut().push(global); } /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, - /// an array of i8*. + /// an array of ptr. fn add_compiler_used_global(&self, global: &'ll Value) { - let cast = unsafe { llvm::LLVMConstPointerCast(global, self.type_i8p()) }; - self.compiler_used_statics.borrow_mut().push(cast); + self.compiler_used_statics.borrow_mut().push(global); } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index e1e0a442845..cb093996d1d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -33,6 +33,7 @@ use rustc_target::abi::{ use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel}; use smallvec::SmallVec; +use libc::c_uint; use std::cell::{Cell, RefCell}; use std::ffi::CStr; use std::str; @@ -58,17 +59,6 @@ pub struct CodegenCx<'ll, 'tcx> { /// Cache of constant strings, pub const_str_cache: RefCell<FxHashMap<String, &'ll Value>>, - /// Reverse-direction for const ptrs cast from globals. - /// - /// Key is a Value holding a `*T`, - /// Val is a Value holding a `*[T]`. - /// - /// Needed because LLVM loses pointer->pointee association - /// when we ptrcast, and we have to ptrcast during codegen - /// of a `[T]` const because we form a slice, a `(*T,usize)` pair, not - /// a pointer to an LLVM array type. Similar for trait objects. - pub const_unsized: RefCell<FxHashMap<&'ll Value, &'ll Value>>, - /// Cache of emitted const globals (value -> global) pub const_globals: RefCell<FxHashMap<&'ll Value, &'ll Value>>, @@ -349,6 +339,23 @@ pub unsafe fn create_module<'ll>( ); } + // Insert `llvm.ident` metadata. + // + // On the wasm targets it will get hooked up to the "producer" sections + // `processed-by` information. + let rustc_producer = + format!("rustc version {}", option_env!("CFG_VERSION").expect("CFG_VERSION")); + let name_metadata = llvm::LLVMMDStringInContext( + llcx, + rustc_producer.as_ptr().cast(), + rustc_producer.as_bytes().len() as c_uint, + ); + llvm::LLVMAddNamedMetadataOperand( + llmod, + cstr!("llvm.ident").as_ptr(), + llvm::LLVMMDNodeInContext(llcx, &name_metadata, 1), + ); + llmod } @@ -446,7 +453,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { instances: Default::default(), vtables: Default::default(), const_str_cache: Default::default(), - const_unsized: Default::default(), const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: RefCell::new(Vec::new()), @@ -477,7 +483,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) { let section = cstr!("llvm.metadata"); - let array = self.const_array(self.type_ptr_to(self.type_i8()), values); + let array = self.const_array(self.type_ptr(), values); unsafe { let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); @@ -655,7 +661,7 @@ impl<'ll> CodegenCx<'ll, '_> { ($($field_ty:expr),*) => (self.type_struct( &[$($field_ty),*], false)) } - let i8p = self.type_i8p(); + let ptr = self.type_ptr(); let void = self.type_void(); let i1 = self.type_i1(); let t_i8 = self.type_i8(); @@ -669,7 +675,7 @@ impl<'ll> CodegenCx<'ll, '_> { let t_metadata = self.type_metadata(); let t_token = self.type_token(); - ifn!("llvm.wasm.get.exception", fn(t_token) -> i8p); + ifn!("llvm.wasm.get.exception", fn(t_token) -> ptr); ifn!("llvm.wasm.get.ehselector", fn(t_token) -> t_i32); ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32); @@ -705,7 +711,7 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); - ifn!("llvm.frameaddress", fn(t_i32) -> i8p); + ifn!("llvm.frameaddress", fn(t_i32) -> ptr); ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); @@ -872,43 +878,43 @@ impl<'ll> CodegenCx<'ll, '_> { ifn!("llvm.usub.sat.i64", fn(t_i64, t_i64) -> t_i64); ifn!("llvm.usub.sat.i128", fn(t_i128, t_i128) -> t_i128); - ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void); - ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void); + ifn!("llvm.lifetime.start.p0i8", fn(t_i64, ptr) -> void); + ifn!("llvm.lifetime.end.p0i8", fn(t_i64, ptr) -> void); ifn!("llvm.expect.i1", fn(i1, i1) -> i1); - ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); + ifn!("llvm.eh.typeid.for", fn(ptr) -> t_i32); ifn!("llvm.localescape", fn(...) -> void); - ifn!("llvm.localrecover", fn(i8p, i8p, t_i32) -> i8p); - ifn!("llvm.x86.seh.recoverfp", fn(i8p, i8p) -> i8p); + ifn!("llvm.localrecover", fn(ptr, ptr, t_i32) -> ptr); + ifn!("llvm.x86.seh.recoverfp", fn(ptr, ptr) -> ptr); ifn!("llvm.assume", fn(i1) -> void); - ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void); + ifn!("llvm.prefetch", fn(ptr, t_i32, t_i32, t_i32) -> void); // This isn't an "LLVM intrinsic", but LLVM's optimization passes // recognize it like one and we assume it exists in `core::slice::cmp` match self.sess().target.arch.as_ref() { - "avr" | "msp430" => ifn!("memcmp", fn(i8p, i8p, t_isize) -> t_i16), - _ => ifn!("memcmp", fn(i8p, i8p, t_isize) -> t_i32), + "avr" | "msp430" => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i16), + _ => ifn!("memcmp", fn(ptr, ptr, t_isize) -> t_i32), } // variadic intrinsics - ifn!("llvm.va_start", fn(i8p) -> void); - ifn!("llvm.va_end", fn(i8p) -> void); - ifn!("llvm.va_copy", fn(i8p, i8p) -> void); + ifn!("llvm.va_start", fn(ptr) -> void); + ifn!("llvm.va_end", fn(ptr) -> void); + ifn!("llvm.va_copy", fn(ptr, ptr) -> void); if self.sess().instrument_coverage() { - ifn!("llvm.instrprof.increment", fn(i8p, t_i64, t_i32, t_i32) -> void); + ifn!("llvm.instrprof.increment", fn(ptr, t_i64, t_i32, t_i32) -> void); } - ifn!("llvm.type.test", fn(i8p, t_metadata) -> i1); - ifn!("llvm.type.checked.load", fn(i8p, t_i32, t_metadata) -> mk_struct! {i8p, i1}); + ifn!("llvm.type.test", fn(ptr, t_metadata) -> i1); + ifn!("llvm.type.checked.load", fn(ptr, t_i32, t_metadata) -> mk_struct! {ptr, i1}); if self.sess().opts.debuginfo != DebugInfo::None { ifn!("llvm.dbg.declare", fn(t_metadata, t_metadata) -> void); ifn!("llvm.dbg.value", fn(t_metadata, t_i64, t_metadata) -> void); } - ifn!("llvm.ptrmask", fn(i8p, t_isize) -> i8p); + ifn!("llvm.ptrmask", fn(ptr, t_isize) -> ptr); None } @@ -922,12 +928,10 @@ impl<'ll> CodegenCx<'ll, '_> { let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() { Some(def_id) => self.get_static(def_id), _ => { - let ty = self - .type_struct(&[self.type_ptr_to(self.type_isize()), self.type_i8p()], false); + let ty = self.type_struct(&[self.type_ptr(), self.type_ptr()], false); self.declare_global("rust_eh_catch_typeinfo", ty) } }; - let eh_catch_typeinfo = self.const_bitcast(eh_catch_typeinfo, self.type_i8p()); self.eh_catch_typeinfo.set(Some(eh_catch_typeinfo)); eh_catch_typeinfo } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 1791ce4b315..6ca61aa43d7 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,4 +1,4 @@ -use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; +use rustc_middle::mir::coverage::{CounterId, MappedExpressionIndex}; /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] @@ -36,11 +36,9 @@ impl Counter { Self { kind: CounterKind::Zero, id: 0 } } - /// Constructs a new `Counter` of kind `CounterValueReference`, and converts - /// the given 1-based counter_id to the required 0-based equivalent for - /// the `Counter` encoding. - pub fn counter_value_reference(counter_id: CounterValueReference) -> Self { - Self { kind: CounterKind::CounterValueReference, id: counter_id.zero_based_index() } + /// Constructs a new `Counter` of kind `CounterValueReference`. + pub fn counter_value_reference(counter_id: CounterId) -> Self { + Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() } } /// Constructs a new `Counter` of kind `Expression`. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs index 06844afd6b8..7e981af0a53 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs @@ -3,24 +3,22 @@ pub use super::ffi::*; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::bug; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, - InjectedExpressionIndex, MappedExpressionIndex, Op, + CodeRegion, CounterId, ExpressionId, MappedExpressionIndex, Op, Operand, }; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; #[derive(Clone, Debug, PartialEq)] pub struct Expression { - lhs: ExpressionOperandId, + lhs: Operand, op: Op, - rhs: ExpressionOperandId, + rhs: Operand, region: Option<CodeRegion>, } /// Collects all of the coverage regions associated with (a) injected counters, (b) counter /// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero), -/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they -/// can both be operands in an expression. This struct also stores the `function_source_hash`, +/// for a given Function. This struct also stores the `function_source_hash`, /// computed during instrumentation, and forwarded with counters. /// /// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap @@ -34,8 +32,8 @@ pub struct FunctionCoverage<'tcx> { instance: Instance<'tcx>, source_hash: u64, is_used: bool, - counters: IndexVec<CounterValueReference, Option<CodeRegion>>, - expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>, + counters: IndexVec<CounterId, Option<CodeRegion>>, + expressions: IndexVec<ExpressionId, Option<Expression>>, unreachable_regions: Vec<CodeRegion>, } @@ -82,48 +80,36 @@ impl<'tcx> FunctionCoverage<'tcx> { } /// Adds a code region to be counted by an injected counter intrinsic. - pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { + pub fn add_counter(&mut self, id: CounterId, region: CodeRegion) { if let Some(previous_region) = self.counters[id].replace(region.clone()) { assert_eq!(previous_region, region, "add_counter: code region for id changed"); } } /// Both counters and "counter expressions" (or simply, "expressions") can be operands in other - /// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression - /// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in - /// any order, and expressions can still be assigned contiguous (though descending) IDs, without - /// knowing what the last counter ID will be. - /// - /// When storing the expression data in the `expressions` vector in the `FunctionCoverage` - /// struct, its vector index is computed, from the given expression ID, by subtracting from - /// `u32::MAX`. - /// - /// Since the expression operands (`lhs` and `rhs`) can reference either counters or - /// expressions, an operand that references an expression also uses its original ID, descending - /// from `u32::MAX`. Theses operands are translated only during code generation, after all - /// counters and expressions have been added. + /// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity + /// between operands that are counter IDs and operands that are expression IDs. pub fn add_counter_expression( &mut self, - expression_id: InjectedExpressionId, - lhs: ExpressionOperandId, + expression_id: ExpressionId, + lhs: Operand, op: Op, - rhs: ExpressionOperandId, + rhs: Operand, region: Option<CodeRegion>, ) { debug!( "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", expression_id, lhs, op, rhs, region ); - let expression_index = self.expression_index(u32::from(expression_id)); debug_assert!( - expression_index.as_usize() < self.expressions.len(), - "expression_index {} is out of range for expressions.len() = {} + expression_id.as_usize() < self.expressions.len(), + "expression_id {} is out of range for expressions.len() = {} for {:?}", - expression_index.as_usize(), + expression_id.as_usize(), self.expressions.len(), self, ); - if let Some(previous_expression) = self.expressions[expression_index].replace(Expression { + if let Some(previous_expression) = self.expressions[expression_id].replace(Expression { lhs, op, rhs, @@ -186,14 +172,11 @@ impl<'tcx> FunctionCoverage<'tcx> { // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type - // and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range - // of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value - // matches the injected counter index); and any other value is converted into a - // `CounterKind::Expression` with the expression's `new_index`. + // and value. // // Expressions will be returned from this function in a sequential vector (array) of // `CounterExpression`, so the expression IDs must be mapped from their original, - // potentially sparse set of indexes, originally in reverse order from `u32::MAX`. + // potentially sparse set of indexes. // // An `Expression` as an operand will have already been encountered as an `Expression` with // operands, so its new_index will already have been generated (as a 1-up index value). @@ -206,34 +189,19 @@ impl<'tcx> FunctionCoverage<'tcx> { // `expression_index`s lower than the referencing `Expression`. Therefore, it is // reasonable to look up the new index of an expression operand while the `new_indexes` // vector is only complete up to the current `ExpressionIndex`. - let id_to_counter = |new_indexes: &IndexSlice< - InjectedExpressionIndex, - Option<MappedExpressionIndex>, - >, - id: ExpressionOperandId| { - if id == ExpressionOperandId::ZERO { - Some(Counter::zero()) - } else if id.index() < self.counters.len() { - debug_assert!( - id.index() > 0, - "ExpressionOperandId indexes for counters are 1-based, but this id={}", - id.index() - ); - // Note: Some codegen-injected Counters may be only referenced by `Expression`s, - // and may not have their own `CodeRegion`s, - let index = CounterValueReference::from(id.index()); - // Note, the conversion to LLVM `Counter` adjusts the index to be zero-based. - Some(Counter::counter_value_reference(index)) - } else { - let index = self.expression_index(u32::from(id)); + type NewIndexes = IndexSlice<ExpressionId, Option<MappedExpressionIndex>>; + let id_to_counter = |new_indexes: &NewIndexes, operand: Operand| match operand { + Operand::Zero => Some(Counter::zero()), + Operand::Counter(id) => Some(Counter::counter_value_reference(id)), + Operand::Expression(id) => { self.expressions - .get(index) + .get(id) .expect("expression id is out of range") .as_ref() // If an expression was optimized out, assume it would have produced a count // of zero. This ensures that expressions dependent on optimized-out // expressions are still valid. - .map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression)) + .map_or(Some(Counter::zero()), |_| new_indexes[id].map(Counter::expression)) } }; @@ -340,9 +308,4 @@ impl<'tcx> FunctionCoverage<'tcx> { fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> { self.unreachable_regions.iter().map(|region| (Counter::zero(), region)) } - - fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex { - debug_assert!(id_descending_from_max >= self.counters.len() as u32); - InjectedExpressionIndex::from(u32::MAX - id_descending_from_max) - } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index 6e815ab3ff5..9149b06886b 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -100,9 +100,11 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Generate the LLVM IR representation of the coverage map and store it in a well-known global let cov_data_val = mapgen.generate_coverage_map(cx, version, filenames_size, filenames_val); + let covfun_section_name = coverageinfo::covfun_section_name(cx); for (mangled_function_name, source_hash, is_used, coverage_mapping_buffer) in function_data { save_function_record( cx, + &covfun_section_name, mangled_function_name, source_hash, filenames_ref, @@ -228,6 +230,7 @@ impl CoverageMapGenerator { /// specific, well-known section and name. fn save_function_record( cx: &CodegenCx<'_, '_>, + covfun_section_name: &str, mangled_function_name: &str, source_hash: u64, filenames_ref: u64, @@ -254,7 +257,13 @@ fn save_function_record( /*packed=*/ true, ); - coverageinfo::save_func_record_to_mod(cx, func_name_hash, func_record_val, is_used); + coverageinfo::save_func_record_to_mod( + cx, + covfun_section_name, + func_name_hash, + func_record_val, + is_used, + ); } /// When finalizing the coverage map, `FunctionCoverage` only has the `CodeRegion`s and counters for diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 1f8f4033931..c1017ab8d7c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -16,9 +16,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_llvm::RustString; use rustc_middle::bug; -use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, CoverageKind, ExpressionOperandId, InjectedExpressionId, Op, -}; +use rustc_middle::mir::coverage::{CodeRegion, CounterId, CoverageKind, ExpressionId, Op, Operand}; use rustc_middle::mir::Coverage; use rustc_middle::ty; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; @@ -33,7 +31,7 @@ mod ffi; pub(crate) mod map_data; pub mod mapgen; -const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START; +const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START; const VAR_ALIGN_BYTES: usize = 8; @@ -125,7 +123,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let fn_name = bx.get_pgo_func_name_var(instance); let hash = bx.const_u64(function_source_hash); let num_counters = bx.const_u32(coverageinfo.num_counters); - let index = bx.const_u32(id.zero_based_index()); + let index = bx.const_u32(id.as_u32()); debug!( "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", fn_name, hash, num_counters, index, @@ -178,7 +176,7 @@ impl<'tcx> Builder<'_, '_, 'tcx> { fn add_coverage_counter( &mut self, instance: Instance<'tcx>, - id: CounterValueReference, + id: CounterId, region: CodeRegion, ) -> bool { if let Some(coverage_context) = self.coverage_context() { @@ -202,10 +200,10 @@ impl<'tcx> Builder<'_, '_, 'tcx> { fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionId, - lhs: ExpressionOperandId, + id: ExpressionId, + lhs: Operand, op: Op, - rhs: ExpressionOperandId, + rhs: Operand, region: Option<CodeRegion>, ) -> bool { if let Some(coverage_context) = self.coverage_context() { @@ -408,6 +406,7 @@ pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>( pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, + covfun_section_name: &str, func_name_hash: u64, func_record_val: &'ll llvm::Value, is_used: bool, @@ -423,20 +422,33 @@ pub(crate) fn save_func_record_to_mod<'ll, 'tcx>( let func_record_var_name = format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }); debug!("function record var name: {:?}", func_record_var_name); - - let func_record_section_name = llvm::build_string(|s| unsafe { - llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); - }) - .expect("Rust Coverage function record section name failed UTF-8 conversion"); - debug!("function record section name: {:?}", func_record_section_name); + debug!("function record section name: {:?}", covfun_section_name); let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name); llvm::set_initializer(llglobal, func_record_val); llvm::set_global_constant(llglobal, true); llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); llvm::set_visibility(llglobal, llvm::Visibility::Hidden); - llvm::set_section(llglobal, &func_record_section_name); + llvm::set_section(llglobal, covfun_section_name); llvm::set_alignment(llglobal, VAR_ALIGN_BYTES); llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); cx.add_used_global(llglobal); } + +/// Returns the section name string to pass through to the linker when embedding +/// per-function coverage information in the object file, according to the target +/// platform's object file format. +/// +/// LLVM's coverage tools read coverage mapping details from this section when +/// producing coverage reports. +/// +/// Typical values are: +/// - `__llvm_covfun` on Linux +/// - `__LLVM_COV,__llvm_covfun` on macOS (includes `__LLVM_COV,` segment prefix) +/// - `.lcovfun$M` on Windows (includes `$M` sorting suffix) +pub(crate) fn covfun_section_name(cx: &CodegenCx<'_, '_>) -> String { + llvm::build_string(|s| unsafe { + llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s); + }) + .expect("Rust Coverage function record section name failed UTF-8 conversion") +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 37f30917609..ef7c661936a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -17,8 +17,7 @@ use rustc_span::symbol::sym; /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &mut Builder<'_, '_, '_>) { if needs_gdb_debug_scripts_section(bx) { - let gdb_debug_scripts_section = - bx.const_bitcast(get_or_insert_gdb_debug_scripts_section_global(bx), bx.type_i8p()); + let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx); // Load just the first byte as that's all that's necessary to force // LLVM to keep around the reference to the global. let volatile_load_instruction = bx.volatile_load(bx.type_i8(), gdb_debug_scripts_section); @@ -54,7 +53,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, ' // The initial byte `4` instructs GDB that the following pretty printer // is defined inline as opposed to in a standalone file. section_contents.extend_from_slice(b"\x04"); - let vis_name = format!("pretty-printer-{}-{}\n", crate_name, index); + let vis_name = format!("pretty-printer-{crate_name}-{index}\n"); section_contents.extend_from_slice(vis_name.as_bytes()); section_contents.extend_from_slice(&visualizer.src); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index c6996f2e16a..40f0bcfdf58 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -184,9 +184,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( debug_assert_eq!( (data_layout.pointer_size, data_layout.pointer_align.abi), cx.size_and_align_of(ptr_type), - "ptr_type={}, pointee_type={}", - ptr_type, - pointee_type, + "ptr_type={ptr_type}, pointee_type={pointee_type}", ); let di_node = unsafe { @@ -521,7 +519,7 @@ fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll D fn hex_encode(data: &[u8]) -> String { let mut hex_string = String::with_capacity(data.len() * 2); for byte in data.iter() { - write!(&mut hex_string, "{:02x}", byte).unwrap(); + write!(&mut hex_string, "{byte:02x}").unwrap(); } hex_string } @@ -766,7 +764,7 @@ fn build_param_type_di_node<'ll, 'tcx>( t: Ty<'tcx>, ) -> DINodeCreationResult<'ll> { debug!("build_param_type_di_node: {:?}", t); - let name = format!("{:?}", t); + let name = format!("{t:?}"); DINodeCreationResult { di_node: unsafe { llvm::LLVMRustDIBuilderCreateBasicType( @@ -814,7 +812,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( debug!("build_compile_unit_di_node: {:?}", name_in_debuginfo); let rustc_producer = format!("rustc version {}", tcx.sess.cfg_version); // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. - let producer = format!("clang LLVM ({})", rustc_producer); + let producer = format!("clang LLVM ({rustc_producer})"); let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped); @@ -888,21 +886,6 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), val); } - // Insert `llvm.ident` metadata on the wasm targets since that will - // get hooked up to the "producer" sections `processed-by` information. - if tcx.sess.target.is_like_wasm { - let name_metadata = llvm::LLVMMDStringInContext( - debug_context.llcontext, - rustc_producer.as_ptr().cast(), - rustc_producer.as_bytes().len() as c_uint, - ); - llvm::LLVMAddNamedMetadataOperand( - debug_context.llmod, - cstr!("llvm.ident").as_ptr(), - llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1), - ); - } - return unit_metadata; }; @@ -1346,10 +1329,10 @@ fn build_vtable_type_di_node<'ll, 'tcx>( // Note: This code does not try to give a proper name to each method // because their might be multiple methods with the same name // (coming from different traits). - (format!("__method{}", index), void_pointer_type_di_node) + (format!("__method{index}"), void_pointer_type_di_node) } ty::VtblEntry::TraitVPtr(_) => { - (format!("__super_trait_ptr{}", index), void_pointer_type_di_node) + (format!("__super_trait_ptr{index}"), void_pointer_type_di_node) } ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_di_node), ty::VtblEntry::MetadataSize => ("size".to_string(), usize_di_node), @@ -1519,5 +1502,5 @@ pub fn tuple_field_name(field_index: usize) -> Cow<'static, str> { TUPLE_FIELD_NAMES .get(field_index) .map(|s| Cow::from(*s)) - .unwrap_or_else(|| Cow::from(format!("__{}", field_index))) + .unwrap_or_else(|| Cow::from(format!("__{field_index}"))) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 7be83638676..c758010c581 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -91,8 +91,7 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>( // For all other pointee types we should already have returned None // at the beginning of the function. panic!( - "fat_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {:?}", - pointee_tail_ty + "fat_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {pointee_tail_ty:?}" ) } } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 44869ced1ae..fced6d504d2 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -81,6 +81,8 @@ pub(crate) struct ErrorCallingDllTool<'a> { #[derive(Diagnostic)] #[diag(codegen_llvm_dlltool_fail_import_library)] pub(crate) struct DlltoolFailImportLibrary<'a> { + pub dlltool_path: Cow<'a, str>, + pub dlltool_args: String, pub stdout: Cow<'a, str>, pub stderr: Cow<'a, str>, } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 6df1b708ccd..86c39ab5e94 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -167,7 +167,6 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { let ptr = args[0].immediate(); let load = if let PassMode::Cast(ty, _) = &fn_abi.ret.mode { let llty = ty.llvm_type(self); - let ptr = self.pointercast(ptr, self.type_ptr_to(llty)); self.volatile_load(llty, ptr) } else { self.volatile_load(self.layout_of(tp_ty).llvm_type(self), ptr) @@ -230,22 +229,22 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::ctlz | sym::cttz => { let y = self.const_bool(false); self.call_intrinsic( - &format!("llvm.{}.i{}", name, width), + &format!("llvm.{name}.i{width}"), &[args[0].immediate(), y], ) } sym::ctlz_nonzero => { let y = self.const_bool(true); - let llvm_name = &format!("llvm.ctlz.i{}", width); + let llvm_name = &format!("llvm.ctlz.i{width}"); self.call_intrinsic(llvm_name, &[args[0].immediate(), y]) } sym::cttz_nonzero => { let y = self.const_bool(true); - let llvm_name = &format!("llvm.cttz.i{}", width); + let llvm_name = &format!("llvm.cttz.i{width}"); self.call_intrinsic(llvm_name, &[args[0].immediate(), y]) } sym::ctpop => self.call_intrinsic( - &format!("llvm.ctpop.i{}", width), + &format!("llvm.ctpop.i{width}"), &[args[0].immediate()], ), sym::bswap => { @@ -253,13 +252,13 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { args[0].immediate() // byte swap a u8/i8 is just a no-op } else { self.call_intrinsic( - &format!("llvm.bswap.i{}", width), + &format!("llvm.bswap.i{width}"), &[args[0].immediate()], ) } } sym::bitreverse => self.call_intrinsic( - &format!("llvm.bitreverse.i{}", width), + &format!("llvm.bitreverse.i{width}"), &[args[0].immediate()], ), sym::rotate_left | sym::rotate_right => { @@ -317,18 +316,12 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.const_bool(true) } else if use_integer_compare { let integer_ty = self.type_ix(layout.size().bits()); - let ptr_ty = self.type_ptr_to(integer_ty); - let a_ptr = self.bitcast(a, ptr_ty); - let a_val = self.load(integer_ty, a_ptr, layout.align().abi); - let b_ptr = self.bitcast(b, ptr_ty); - let b_val = self.load(integer_ty, b_ptr, layout.align().abi); + let a_val = self.load(integer_ty, a, layout.align().abi); + let b_val = self.load(integer_ty, b, layout.align().abi); self.icmp(IntPredicate::IntEQ, a_val, b_val) } else { - let i8p_ty = self.type_i8p(); - let a_ptr = self.bitcast(a, i8p_ty); - let b_ptr = self.bitcast(b, i8p_ty); let n = self.const_usize(layout.size().bytes()); - let cmp = self.call_intrinsic("memcmp", &[a_ptr, b_ptr, n]); + let cmp = self.call_intrinsic("memcmp", &[a, b, n]); match self.cx.sess().target.arch.as_ref() { "avr" | "msp430" => self.icmp(IntPredicate::IntEQ, cmp, self.const_i16(0)), _ => self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)), @@ -383,10 +376,8 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; if !fn_abi.ret.is_ignore() { - if let PassMode::Cast(ty, _) = &fn_abi.ret.mode { - let ptr_llty = self.type_ptr_to(ty.llvm_type(self)); - let ptr = self.pointercast(result.llval, ptr_llty); - self.store(llval, ptr, result.align); + if let PassMode::Cast(_, _) = &fn_abi.ret.mode { + self.store(llval, result.llval, result.align); } else { OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) .val @@ -410,9 +401,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn type_test(&mut self, pointer: Self::Value, typeid: Self::Value) -> Self::Value { // Test the called operand using llvm.type.test intrinsic. The LowerTypeTests link-time // optimization pass replaces calls to this intrinsic with code to test type membership. - let i8p_ty = self.type_i8p(); - let bitcast = self.bitcast(pointer, i8p_ty); - self.call_intrinsic("llvm.type.test", &[bitcast, typeid]) + self.call_intrinsic("llvm.type.test", &[pointer, typeid]) } fn type_checked_load( @@ -444,7 +433,7 @@ fn try_intrinsic<'ll>( dest: &'ll Value, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { - let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void()); + let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.call(try_func_ty, None, None, try_func, &[data], None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. @@ -544,8 +533,8 @@ fn codegen_msvc_try<'ll>( // // More information can be found in libstd's seh.rs implementation. let ptr_align = bx.tcx().data_layout.pointer_align.abi; - let slot = bx.alloca(bx.type_i8p(), ptr_align); - let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void()); + let slot = bx.alloca(bx.type_ptr(), ptr_align); + let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); bx.switch_to_block(normal); @@ -568,10 +557,10 @@ fn codegen_msvc_try<'ll>( // // When modifying, make sure that the type_name string exactly matches // the one used in library/panic_unwind/src/seh.rs. - let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p()); + let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_ptr()); let type_name = bx.const_bytes(b"rust_panic\0"); let type_info = - bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_i8p()), type_name], false); + bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_ptr()), type_name], false); let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); unsafe { llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); @@ -588,15 +577,15 @@ fn codegen_msvc_try<'ll>( bx.switch_to_block(catchpad_rust); let flags = bx.const_i32(8); let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]); - let ptr = bx.load(bx.type_i8p(), slot, ptr_align); - let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); + let ptr = bx.load(bx.type_ptr(), slot, ptr_align); + let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); bx.catch_ret(&funclet, caught); // The flag value of 64 indicates a "catch-all". bx.switch_to_block(catchpad_foreign); let flags = bx.const_i32(64); - let null = bx.const_null(bx.type_i8p()); + let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null, flags, null]); bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet)); bx.catch_ret(&funclet, caught); @@ -655,7 +644,7 @@ fn codegen_wasm_try<'ll>( // ret i32 1 // } // - let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void()); + let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); bx.switch_to_block(normal); @@ -665,13 +654,13 @@ fn codegen_wasm_try<'ll>( let cs = bx.catch_switch(None, None, &[catchpad]); bx.switch_to_block(catchpad); - let null = bx.const_null(bx.type_i8p()); + let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null]); let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]); let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]); - let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); + let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); bx.catch_ret(&funclet, caught); @@ -723,7 +712,7 @@ fn codegen_gnu_try<'ll>( let try_func = llvm::get_param(bx.llfn(), 0); let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); - let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void()); + let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); bx.switch_to_block(then); @@ -736,12 +725,12 @@ fn codegen_gnu_try<'ll>( // the landing pad clauses the exception's type had been matched to. // rust_try ignores the selector. bx.switch_to_block(catch); - let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); + let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false); let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 1); - let tydesc = bx.const_null(bx.type_i8p()); + let tydesc = bx.const_null(bx.type_ptr()); bx.add_clause(vals, tydesc); let ptr = bx.extract_value(vals, 0); - let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); + let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); bx.ret(bx.const_i32(1)); }); @@ -787,7 +776,7 @@ fn codegen_emcc_try<'ll>( let try_func = llvm::get_param(bx.llfn(), 0); let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); - let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void()); + let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); bx.switch_to_block(then); @@ -800,10 +789,10 @@ fn codegen_emcc_try<'ll>( // the landing pad clauses the exception's type had been matched to. bx.switch_to_block(catch); let tydesc = bx.eh_catch_typeinfo(); - let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); + let lpad_ty = bx.type_struct(&[bx.type_ptr(), bx.type_i32()], false); let vals = bx.landing_pad(lpad_ty, bx.eh_personality(), 2); bx.add_clause(vals, tydesc); - bx.add_clause(vals, bx.const_null(bx.type_i8p())); + bx.add_clause(vals, bx.const_null(bx.type_ptr())); let ptr = bx.extract_value(vals, 0); let selector = bx.extract_value(vals, 1); @@ -816,7 +805,7 @@ fn codegen_emcc_try<'ll>( // create an alloca and pass a pointer to that. let ptr_align = bx.tcx().data_layout.pointer_align.abi; let i8_align = bx.tcx().data_layout.i8_align.abi; - let catch_data_type = bx.type_struct(&[bx.type_i8p(), bx.type_bool()], false); + let catch_data_type = bx.type_struct(&[bx.type_ptr(), bx.type_bool()], false); let catch_data = bx.alloca(catch_data_type, ptr_align); let catch_data_0 = bx.inbounds_gep(catch_data_type, catch_data, &[bx.const_usize(0), bx.const_usize(0)]); @@ -824,9 +813,8 @@ fn codegen_emcc_try<'ll>( let catch_data_1 = bx.inbounds_gep(catch_data_type, catch_data, &[bx.const_usize(0), bx.const_usize(1)]); bx.store(is_rust_panic, catch_data_1, i8_align); - let catch_data = bx.bitcast(catch_data, bx.type_i8p()); - let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); + let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None); bx.ret(bx.const_i32(1)); }); @@ -967,8 +955,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let place = PlaceRef::alloca(bx, args[0].layout); args[0].val.store(bx, place); let int_ty = bx.type_ix(expected_bytes * 8); - let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty)); - bx.load(int_ty, ptr, Align::ONE) + bx.load(int_ty, place.llval, Align::ONE) } _ => return_error!(InvalidMonomorphization::InvalidBitmask { span, @@ -1217,7 +1204,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let ptr = bx.alloca(bx.type_ix(expected_bytes * 8), Align::ONE); bx.store(ze, ptr, Align::ONE); let array_ty = bx.type_array(bx.type_i8(), expected_bytes); - let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty)); return Ok(bx.load(array_ty, ptr, Align::ONE)); } _ => return_error!(InvalidMonomorphization::CannotReturn { @@ -1283,7 +1269,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }), }; - let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str); + let llvm_name = &format!("llvm.{intr_name}.v{in_len}{elem_ty_str}"); let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty); let c = bx.call( fn_ty, @@ -1321,50 +1307,34 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // FIXME: use: // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182 // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81 - fn llvm_vector_str( - elem_ty: Ty<'_>, - vec_len: u64, - no_pointers: usize, - bx: &Builder<'_, '_, '_>, - ) -> String { - let p0s: String = "p0".repeat(no_pointers); + fn llvm_vector_str(bx: &Builder<'_, '_, '_>, elem_ty: Ty<'_>, vec_len: u64) -> String { match *elem_ty.kind() { ty::Int(v) => format!( - "v{}{}i{}", + "v{}i{}", vec_len, - p0s, // Normalize to prevent crash if v: IntTy::Isize v.normalize(bx.target_spec().pointer_width).bit_width().unwrap() ), ty::Uint(v) => format!( - "v{}{}i{}", + "v{}i{}", vec_len, - p0s, // Normalize to prevent crash if v: UIntTy::Usize v.normalize(bx.target_spec().pointer_width).bit_width().unwrap() ), - ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()), + ty::Float(v) => format!("v{}f{}", vec_len, v.bit_width()), + ty::RawPtr(_) => format!("v{}p0", vec_len), _ => unreachable!(), } } - fn llvm_vector_ty<'ll>( - cx: &CodegenCx<'ll, '_>, - elem_ty: Ty<'_>, - vec_len: u64, - mut no_pointers: usize, - ) -> &'ll Type { - // FIXME: use cx.layout_of(ty).llvm_type() ? - let mut elem_ty = match *elem_ty.kind() { + fn llvm_vector_ty<'ll>(cx: &CodegenCx<'ll, '_>, elem_ty: Ty<'_>, vec_len: u64) -> &'ll Type { + let elem_ty = match *elem_ty.kind() { ty::Int(v) => cx.type_int_from_ty(v), ty::Uint(v) => cx.type_uint_from_ty(v), ty::Float(v) => cx.type_float_from_ty(v), + ty::RawPtr(_) => cx.type_ptr(), _ => unreachable!(), }; - while no_pointers > 0 { - elem_ty = cx.type_ptr_to(elem_ty); - no_pointers -= 1; - } cx.type_vector(elem_ty, vec_len) } @@ -1419,47 +1389,26 @@ fn generic_simd_intrinsic<'ll, 'tcx>( InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty } ); - // This counts how many pointers - fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), - _ => 0, - } - } - - // Non-ptr type - fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), - _ => t, - } - } - // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), - _ => { - require!( - false, - InvalidMonomorphization::ExpectedElementType { - span, - name, - expected_element: element_ty1, - second_arg: arg_tys[1], - in_elem, - in_ty, - mutability: ExpectedPointerMutability::Not, - } - ); - unreachable!(); + + require!( + matches!( + element_ty1.kind(), + ty::RawPtr(p) if p.ty == in_elem && p.ty.kind() == element_ty0.kind() + ), + InvalidMonomorphization::ExpectedElementType { + span, + name, + expected_element: element_ty1, + second_arg: arg_tys[1], + in_elem, + in_ty, + mutability: ExpectedPointerMutability::Not, } - }; - assert!(pointer_count > 0); - assert_eq!(pointer_count - 1, ptr_count(element_ty0)); - assert_eq!(underlying_ty, non_ptr(element_ty0)); + ); // The element type of the third argument must be a signed integer type of any width: let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); @@ -1490,15 +1439,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }; // Type of the vector of pointers: - let llvm_pointer_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count); - let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count, bx); + let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len); + let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len); // Type of the vector of elements: - let llvm_elem_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count - 1); - let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1, bx); + let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); + let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len); let llvm_intrinsic = - format!("llvm.masked.gather.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str); + format!("llvm.masked.gather.{llvm_elem_vec_str}.{llvm_pointer_vec_str}"); let fn_ty = bx.type_func( &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty], llvm_elem_vec_ty, @@ -1559,50 +1508,28 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - // This counts how many pointers - fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), - _ => 0, - } - } - - // Non-ptr type - fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), - _ => t, - } - } - // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem && p.mutbl.is_mut() => { - (ptr_count(element_ty1), non_ptr(element_ty1)) - } - _ => { - require!( - false, - InvalidMonomorphization::ExpectedElementType { - span, - name, - expected_element: element_ty1, - second_arg: arg_tys[1], - in_elem, - in_ty, - mutability: ExpectedPointerMutability::Mut, - } - ); - unreachable!(); + + require!( + matches!( + element_ty1.kind(), + ty::RawPtr(p) + if p.ty == in_elem && p.mutbl.is_mut() && p.ty.kind() == element_ty0.kind() + ), + InvalidMonomorphization::ExpectedElementType { + span, + name, + expected_element: element_ty1, + second_arg: arg_tys[1], + in_elem, + in_ty, + mutability: ExpectedPointerMutability::Mut, } - }; - assert!(pointer_count > 0); - assert_eq!(pointer_count - 1, ptr_count(element_ty0)); - assert_eq!(underlying_ty, non_ptr(element_ty0)); + ); // The element type of the third argument must be a signed integer type of any width: match element_ty2.kind() { @@ -1634,15 +1561,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let ret_t = bx.type_void(); // Type of the vector of pointers: - let llvm_pointer_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count); - let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count, bx); + let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len); + let llvm_pointer_vec_str = llvm_vector_str(bx, element_ty1, in_len); // Type of the vector of elements: - let llvm_elem_vec_ty = llvm_vector_ty(bx, underlying_ty, in_len, pointer_count - 1); - let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1, bx); + let llvm_elem_vec_ty = llvm_vector_ty(bx, element_ty0, in_len); + let llvm_elem_vec_str = llvm_vector_str(bx, element_ty0, in_len); let llvm_intrinsic = - format!("llvm.masked.scatter.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str); + format!("llvm.masked.scatter.{llvm_elem_vec_str}.{llvm_pointer_vec_str}"); let fn_ty = bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t); let f = bx.declare_cfn(&llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); @@ -1857,11 +1784,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } } - if in_elem == out_elem { - return Ok(args[0].immediate()); - } else { - return Ok(bx.pointercast(args[0].immediate(), llret_ty)); - } + return Ok(args[0].immediate()); } if name == sym::simd_expose_addr { @@ -2074,6 +1997,52 @@ fn generic_simd_intrinsic<'ll, 'tcx>( simd_neg: Int => neg, Float => fneg; } + // Unary integer intrinsics + if matches!(name, sym::simd_bswap | sym::simd_bitreverse | sym::simd_ctlz | sym::simd_cttz) { + let vec_ty = bx.cx.type_vector( + match *in_elem.kind() { + ty::Int(i) => bx.cx.type_int_from_ty(i), + ty::Uint(i) => bx.cx.type_uint_from_ty(i), + _ => return_error!(InvalidMonomorphization::UnsupportedOperation { + span, + name, + in_ty, + in_elem + }), + }, + in_len as u64, + ); + let intrinsic_name = match name { + sym::simd_bswap => "bswap", + sym::simd_bitreverse => "bitreverse", + sym::simd_ctlz => "ctlz", + sym::simd_cttz => "cttz", + _ => unreachable!(), + }; + let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits(); + let llvm_intrinsic = &format!("llvm.{}.v{}i{}", intrinsic_name, in_len, int_size,); + + return if name == sym::simd_bswap && int_size == 8 { + // byte swap is no-op for i8/u8 + Ok(args[0].immediate()) + } else if matches!(name, sym::simd_ctlz | sym::simd_cttz) { + let fn_ty = bx.type_func(&[vec_ty, bx.type_i1()], vec_ty); + let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); + Ok(bx.call( + fn_ty, + None, + None, + f, + &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], + None, + )) + } else { + let fn_ty = bx.type_func(&[vec_ty], vec_ty); + let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None)) + }; + } + if name == sym::simd_arith_offset { // This also checks that the first operand is a ptr type. let pointee = in_elem.builtin_deref(true).unwrap_or_else(|| { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 24ba28bbc82..d283299ac46 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -28,7 +28,7 @@ pub use llvm_util::target_features; use rustc_ast::expand::allocator::AllocatorKind; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use rustc_codegen_ssa::back::write::{ - CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, + CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; @@ -40,12 +40,13 @@ use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest}; +use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; use std::any::Any; use std::ffi::CStr; +use std::io::Write; mod back { pub mod archive; @@ -140,18 +141,6 @@ impl ExtraBackendMethods for LlvmCodegenBackend { back::write::target_machine_factory(sess, optlvl, target_features) } - fn spawn_thread<F, T>(time_trace: bool, f: F) -> std::thread::JoinHandle<T> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - std::thread::spawn(move || { - let _profiler = TimeTraceProfiler::new(time_trace); - f() - }) - } - fn spawn_named_thread<F, T>( time_trace: bool, name: String, @@ -178,7 +167,28 @@ impl WriteBackendMethods for LlvmCodegenBackend { type ThinBuffer = back::lto::ThinBuffer; fn print_pass_timings(&self) { unsafe { - llvm::LLVMRustPrintPassTimings(); + let mut size = 0; + let cstr = llvm::LLVMRustPrintPassTimings(&mut size as *mut usize); + if cstr.is_null() { + println!("failed to get pass timings"); + } else { + let timings = std::slice::from_raw_parts(cstr as *const u8, size); + std::io::stdout().write_all(timings).unwrap(); + libc::free(cstr as *mut _); + } + } + } + fn print_statistics(&self) { + unsafe { + let mut size = 0; + let cstr = llvm::LLVMRustPrintStatistics(&mut size as *mut usize); + if cstr.is_null() { + println!("failed to get pass stats"); + } else { + let stats = std::slice::from_raw_parts(cstr as *const u8, size); + std::io::stdout().write_all(stats).unwrap(); + libc::free(cstr as *mut _); + } } } fn run_link( @@ -190,7 +200,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { } fn run_fat_lto( cgcx: &CodegenContext<Self>, - modules: Vec<FatLTOInput<Self>>, + modules: Vec<FatLtoInput<Self>>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, ) -> Result<LtoModuleCodegen<Self>, FatalError> { back::lto::run_fat(cgcx, modules, cached_modules) @@ -262,10 +272,10 @@ impl CodegenBackend for LlvmCodegenBackend { |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true) } - fn print(&self, req: PrintRequest, sess: &Session) { - match req { - PrintRequest::RelocationModels => { - println!("Available relocation models:"); + fn print(&self, req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) { + match req.kind { + PrintKind::RelocationModels => { + writeln!(out, "Available relocation models:"); for name in &[ "static", "pic", @@ -276,26 +286,27 @@ impl CodegenBackend for LlvmCodegenBackend { "ropi-rwpi", "default", ] { - println!(" {}", name); + writeln!(out, " {name}"); } - println!(); + writeln!(out); } - PrintRequest::CodeModels => { - println!("Available code models:"); + PrintKind::CodeModels => { + writeln!(out, "Available code models:"); for name in &["tiny", "small", "kernel", "medium", "large"] { - println!(" {}", name); + writeln!(out, " {name}"); } - println!(); + writeln!(out); } - PrintRequest::TlsModels => { - println!("Available TLS models:"); + PrintKind::TlsModels => { + writeln!(out, "Available TLS models:"); for name in &["global-dynamic", "local-dynamic", "initial-exec", "local-exec"] { - println!(" {}", name); + writeln!(out, " {name}"); } - println!(); + writeln!(out); } - PrintRequest::StackProtectorStrategies => { - println!( + PrintKind::StackProtectorStrategies => { + writeln!( + out, r#"Available stack protector strategies: all Generate stack canaries in all functions. @@ -319,7 +330,7 @@ impl CodegenBackend for LlvmCodegenBackend { "# ); } - req => llvm_util::print(req, sess), + _other => llvm_util::print(req, out, sess), } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 605f0154773..7407cfa8c5b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1073,7 +1073,7 @@ extern "C" { // Operations on array, pointer, and vector types (sequence types) pub fn LLVMRustArrayType(ElementType: &Type, ElementCount: u64) -> &Type; - pub fn LLVMPointerType(ElementType: &Type, AddressSpace: c_uint) -> &Type; + pub fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; pub fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; pub fn LLVMGetElementType(Ty: &Type) -> &Type; @@ -1868,7 +1868,10 @@ extern "C" { pub fn LLVMRustGetLastError() -> *const c_char; /// Print the pass timings since static dtors aren't picking them up. - pub fn LLVMRustPrintPassTimings(); + pub fn LLVMRustPrintPassTimings(size: *const size_t) -> *const c_char; + + /// Print the statistics since static dtors aren't picking them up. + pub fn LLVMRustPrintStatistics(size: *const size_t) -> *const c_char; pub fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; @@ -2280,7 +2283,12 @@ extern "C" { pub fn LLVMRustHasFeature(T: &TargetMachine, s: *const c_char) -> bool; - pub fn LLVMRustPrintTargetCPUs(T: &TargetMachine, cpu: *const c_char); + pub fn LLVMRustPrintTargetCPUs( + T: &TargetMachine, + cpu: *const c_char, + print: unsafe extern "C" fn(out: *mut c_void, string: *const c_char, len: usize), + out: *mut c_void, + ); pub fn LLVMRustGetTargetFeaturesCount(T: &TargetMachine) -> size_t; pub fn LLVMRustGetTargetFeature( T: &TargetMachine, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 03be0654b50..a76c9c9b735 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -8,16 +8,17 @@ use libc::c_int; use rustc_codegen_ssa::target_features::{ supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES, }; +use rustc_codegen_ssa::traits::PrintBackendInfo; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::small_c_str::SmallCStr; use rustc_fs_util::path_to_c_string; use rustc_middle::bug; -use rustc_session::config::PrintRequest; +use rustc_session::config::{PrintKind, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use std::ffi::{CStr, CString}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::path::Path; use std::ptr; use std::slice; @@ -110,6 +111,10 @@ unsafe fn configure_llvm(sess: &Session) { // Use non-zero `import-instr-limit` multiplier for cold callsites. add("-import-cold-multiplier=0.1", false); + if sess.print_llvm_stats() { + add("-stats", false); + } + for arg in sess_args { add(&(*arg), true); } @@ -310,7 +315,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { pub fn print_version() { let (major, minor, patch) = get_version(); - println!("LLVM version: {}.{}.{}", major, minor, patch); + println!("LLVM version: {major}.{minor}.{patch}"); } pub fn get_version() -> (u32, u32, u32) { @@ -350,7 +355,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> { ret } -fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { +fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &llvm::TargetMachine) { let mut llvm_target_features = llvm_target_features(tm); let mut known_llvm_target_features = FxHashSet::<&'static str>::default(); let mut rustc_target_features = supported_target_features(sess) @@ -383,36 +388,48 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) { .max() .unwrap_or(0); - println!("Features supported by rustc for this target:"); + writeln!(out, "Features supported by rustc for this target:"); for (feature, desc) in &rustc_target_features { - println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + writeln!(out, " {feature:max_feature_len$} - {desc}."); } - println!("\nCode-generation features supported by LLVM for this target:"); + writeln!(out, "\nCode-generation features supported by LLVM for this target:"); for (feature, desc) in &llvm_target_features { - println!(" {1:0$} - {2}.", max_feature_len, feature, desc); + writeln!(out, " {feature:max_feature_len$} - {desc}."); } if llvm_target_features.is_empty() { - println!(" Target features listing is not supported by this LLVM version."); + writeln!(out, " Target features listing is not supported by this LLVM version."); } - println!("\nUse +feature to enable a feature, or -feature to disable it."); - println!("For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); - println!("Code-generation features cannot be used in cfg or #[target_feature],"); - println!("and may be renamed or removed in a future version of LLVM or rustc.\n"); + writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it."); + writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n"); + writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],"); + writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n"); } -pub(crate) fn print(req: PrintRequest, sess: &Session) { +pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess: &Session) { require_inited(); let tm = create_informational_target_machine(sess); - match req { - PrintRequest::TargetCPUs => { + match req.kind { + PrintKind::TargetCPUs => { // SAFETY generate a C compatible string from a byte slice to pass // the target CPU name into LLVM, the lifetime of the reference is // at least as long as the C function let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref())) .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e)); - unsafe { llvm::LLVMRustPrintTargetCPUs(tm, cpu_cstring.as_ptr()) }; + unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) { + let out = &mut *(out as *mut &mut dyn PrintBackendInfo); + let bytes = slice::from_raw_parts(string as *const u8, len); + write!(out, "{}", String::from_utf8_lossy(bytes)); + } + unsafe { + llvm::LLVMRustPrintTargetCPUs( + tm, + cpu_cstring.as_ptr(), + callback, + &mut out as *mut &mut dyn PrintBackendInfo as *mut c_void, + ); + } } - PrintRequest::TargetFeatures => print_target_features(sess, tm), + PrintKind::TargetFeatures => print_target_features(out, sess, tm), _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), } } @@ -490,8 +507,6 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str .features .split(',') .filter(|v| !v.is_empty() && backend_feature_name(v).is_some()) - // Drop +atomics-32 feature introduced in LLVM 15. - .filter(|v| *v != "+atomics-32" || get_version() >= (15, 0, 0)) .map(String::from), ); @@ -558,7 +573,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str match (enable_disable, feat) { ('-' | '+', TargetFeatureFoldStrength::Both(f)) | ('+', TargetFeatureFoldStrength::EnableOnly(f)) => { - Some(format!("{}{}", enable_disable, f)) + Some(format!("{enable_disable}{f}")) } _ => None, } diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 7e672a8dc33..8db6195d931 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -112,12 +112,6 @@ impl<'ll> CodegenCx<'ll, '_> { } } - pub(crate) fn type_pointee_for_align(&self, align: Align) -> &'ll Type { - // FIXME(eddyb) We could find a better approximation if ity.align < align. - let ity = Integer::approximate_align(self, align); - self.type_from_integer(ity) - } - /// Return a LLVM type that has at most the required alignment, /// and exactly the required size, as a best-effort padding array. pub(crate) fn type_padding_filler(&self, size: Size, align: Align) -> &'ll Type { @@ -189,17 +183,12 @@ impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } } - fn type_ptr_to(&self, ty: &'ll Type) -> &'ll Type { - assert_ne!( - self.type_kind(ty), - TypeKind::Function, - "don't call ptr_to on function types, use ptr_to_llvm_type on FnAbi instead or explicitly specify an address space if it makes sense" - ); - ty.ptr_to(AddressSpace::DATA) + fn type_ptr(&self) -> &'ll Type { + self.type_ptr_ext(AddressSpace::DATA) } - fn type_ptr_to_ext(&self, ty: &'ll Type, address_space: AddressSpace) -> &'ll Type { - ty.ptr_to(address_space) + fn type_ptr_ext(&self, address_space: AddressSpace) -> &'ll Type { + unsafe { llvm::LLVMPointerTypeInContext(self.llcx, address_space.0) } } fn element_type(&self, ty: &'ll Type) -> &'ll Type { @@ -247,12 +236,8 @@ impl Type { unsafe { llvm::LLVMIntTypeInContext(llcx, num_bits as c_uint) } } - pub fn i8p_llcx(llcx: &llvm::Context) -> &Type { - Type::i8_llcx(llcx).ptr_to(AddressSpace::DATA) - } - - fn ptr_to(&self, address_space: AddressSpace) -> &Type { - unsafe { llvm::LLVMPointerType(self, address_space.0) } + pub fn ptr_llcx(llcx: &llvm::Context) -> &Type { + unsafe { llvm::LLVMPointerTypeInContext(llcx, AddressSpace::DATA.0) } } } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 2dbd467cc84..32e71c6a641 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -23,7 +23,7 @@ fn uncached_llvm_type<'a, 'tcx>( match layout.abi { Abi::Scalar(_) => bug!("handled elsewhere"), Abi::Vector { element, count } => { - let element = layout.scalar_llvm_type_at(cx, element, Size::ZERO); + let element = layout.scalar_llvm_type_at(cx, element); return cx.type_vector(element, count); } Abi::ScalarPair(..) => { @@ -179,12 +179,7 @@ pub trait LayoutLlvmExt<'tcx> { fn is_llvm_scalar_pair(&self) -> bool; fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type; fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type; - fn scalar_llvm_type_at<'a>( - &self, - cx: &CodegenCx<'a, 'tcx>, - scalar: Scalar, - offset: Size, - ) -> &'a Type; + fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, scalar: Scalar) -> &'a Type; fn scalar_pair_element_llvm_type<'a>( &self, cx: &CodegenCx<'a, 'tcx>, @@ -230,16 +225,12 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { return llty; } let llty = match *self.ty.kind() { - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - cx.type_ptr_to(cx.layout_of(ty).llvm_type(cx)) - } - ty::Adt(def, _) if def.is_box() => { - cx.type_ptr_to(cx.layout_of(self.ty.boxed_ty()).llvm_type(cx)) - } + ty::Ref(..) | ty::RawPtr(_) => cx.type_ptr(), + ty::Adt(def, _) if def.is_box() => cx.type_ptr(), ty::FnPtr(sig) => { cx.fn_ptr_backend_type(cx.fn_abi_of_fn_ptr(sig, ty::List::empty())) } - _ => self.scalar_llvm_type_at(cx, scalar, Size::ZERO), + _ => self.scalar_llvm_type_at(cx, scalar), }; cx.scalar_lltypes.borrow_mut().insert(self.ty, llty); return llty; @@ -300,25 +291,12 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { self.llvm_type(cx) } - fn scalar_llvm_type_at<'a>( - &self, - cx: &CodegenCx<'a, 'tcx>, - scalar: Scalar, - offset: Size, - ) -> &'a Type { + fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, scalar: Scalar) -> &'a Type { match scalar.primitive() { Int(i, _) => cx.type_from_integer(i), F32 => cx.type_f32(), F64 => cx.type_f64(), - Pointer(address_space) => { - // If we know the alignment, pick something better than i8. - let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { - cx.type_pointee_for_align(pointee.align) - } else { - cx.type_i8() - }; - cx.type_ptr_to_ext(pointee, address_space) - } + Pointer(address_space) => cx.type_ptr_ext(address_space), } } @@ -364,8 +342,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { return cx.type_i1(); } - let offset = if index == 0 { Size::ZERO } else { a.size(cx).align_to(b.align(cx).abi) }; - self.scalar_llvm_type_at(cx, scalar, offset) + self.scalar_llvm_type_at(cx, scalar) } fn llvm_field_index<'a>(&self, cx: &CodegenCx<'a, 'tcx>, index: usize) -> u64 { diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 8800caa71d6..172c66a7af1 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -5,7 +5,7 @@ use crate::value::Value; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::{ common::IntPredicate, - traits::{BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods}, + traits::{BaseTypeMethods, BuilderMethods, ConstMethods}, }; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::Ty; @@ -26,24 +26,18 @@ fn round_pointer_up_to_alignment<'ll>( fn emit_direct_ptr_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, - llty: &'ll Type, size: Size, align: Align, slot_size: Align, allow_higher_align: bool, ) -> (&'ll Value, Align) { - let va_list_ty = bx.type_i8p(); - let va_list_ptr_ty = bx.type_ptr_to(va_list_ty); - let va_list_addr = if list.layout.llvm_type(bx.cx) != va_list_ptr_ty { - bx.bitcast(list.immediate(), va_list_ptr_ty) - } else { - list.immediate() - }; + let va_list_ty = bx.type_ptr(); + let va_list_addr = list.immediate(); let ptr = bx.load(va_list_ty, va_list_addr, bx.tcx().data_layout.pointer_align.abi); let (addr, addr_align) = if allow_higher_align && align > slot_size { - (round_pointer_up_to_alignment(bx, ptr, align, bx.cx().type_i8p()), align) + (round_pointer_up_to_alignment(bx, ptr, align, bx.type_ptr()), align) } else { (ptr, slot_size) }; @@ -56,9 +50,9 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big { let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); let adjusted = bx.inbounds_gep(bx.type_i8(), addr, &[adjusted_size]); - (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align) + (adjusted, addr_align) } else { - (bx.bitcast(addr, bx.cx().type_ptr_to(llty)), addr_align) + (addr, addr_align) } } @@ -81,7 +75,7 @@ fn emit_ptr_va_arg<'ll, 'tcx>( (layout.llvm_type(bx.cx), layout.size, layout.align) }; let (addr, addr_align) = - emit_direct_ptr_va_arg(bx, list, llty, size, align.abi, slot_size, allow_higher_align); + emit_direct_ptr_va_arg(bx, list, size, align.abi, slot_size, allow_higher_align); if indirect { let tmp_ret = bx.load(llty, addr, addr_align); bx.load(bx.cx.layout_of(target_ty).llvm_type(bx.cx), tmp_ret, align.abi) @@ -146,7 +140,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( bx.cond_br(use_stack, on_stack, in_reg); bx.switch_to_block(in_reg); - let top_type = bx.type_i8p(); + let top_type = bx.type_ptr(); let top = bx.struct_gep(va_list_ty, va_list_addr, reg_top_index); let top = bx.load(top_type, top, bx.tcx().data_layout.pointer_align.abi); @@ -158,7 +152,6 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( reg_addr = bx.gep(bx.type_i8(), reg_addr, &[offset]); } let reg_type = layout.llvm_type(bx); - let reg_addr = bx.bitcast(reg_addr, bx.cx.type_ptr_to(reg_type)); let reg_value = bx.load(reg_type, reg_addr, layout.align.abi); bx.br(end); @@ -218,7 +211,7 @@ fn emit_s390x_va_arg<'ll, 'tcx>( // Work out the address of the value in the register save area. let reg_ptr = bx.struct_gep(va_list_ty, va_list_addr, va_list_layout.llvm_field_index(bx.cx, 3)); - let reg_ptr_v = bx.load(bx.type_i8p(), reg_ptr, bx.tcx().data_layout.pointer_align.abi); + let reg_ptr_v = bx.load(bx.type_ptr(), reg_ptr, bx.tcx().data_layout.pointer_align.abi); let scaled_reg_count = bx.mul(reg_count_v, bx.const_u64(8)); let reg_off = bx.add(scaled_reg_count, bx.const_u64(reg_save_index * 8 + reg_padding)); let reg_addr = bx.gep(bx.type_i8(), reg_ptr_v, &[reg_off]); @@ -234,7 +227,7 @@ fn emit_s390x_va_arg<'ll, 'tcx>( // Work out the address of the value in the argument overflow area. let arg_ptr = bx.struct_gep(va_list_ty, va_list_addr, va_list_layout.llvm_field_index(bx.cx, 2)); - let arg_ptr_v = bx.load(bx.type_i8p(), arg_ptr, bx.tcx().data_layout.pointer_align.abi); + let arg_ptr_v = bx.load(bx.type_ptr(), arg_ptr, bx.tcx().data_layout.pointer_align.abi); let arg_off = bx.const_u64(padding); let mem_addr = bx.gep(bx.type_i8(), arg_ptr_v, &[arg_off]); @@ -246,14 +239,12 @@ fn emit_s390x_va_arg<'ll, 'tcx>( // Return the appropriate result. bx.switch_to_block(end); - let val_addr = bx.phi(bx.type_i8p(), &[reg_addr, mem_addr], &[in_reg, in_mem]); + let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]); let val_type = layout.llvm_type(bx); let val_addr = if indirect { - let ptr_type = bx.cx.type_ptr_to(val_type); - let ptr_addr = bx.bitcast(val_addr, bx.cx.type_ptr_to(ptr_type)); - bx.load(ptr_type, ptr_addr, bx.tcx().data_layout.pointer_align.abi) + bx.load(bx.cx.type_ptr(), val_addr, bx.tcx().data_layout.pointer_align.abi) } else { - bx.bitcast(val_addr, bx.cx.type_ptr_to(val_type)) + val_addr }; bx.load(val_type, val_addr, layout.align.abi) } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 984efa21044..6582fd62387 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -17,7 +17,6 @@ tempfile = "3.2" thorin-dwp = "0.6" pathdiff = "0.2.0" serde_json = "1.0.59" -snap = "1" smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } regex = "1.4" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index f73080182bf..b6c70c62249 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -197,6 +197,8 @@ codegen_ssa_specify_libraries_to_link = use the `-l` flag to specify native libr codegen_ssa_static_library_native_artifacts = Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. +codegen_ssa_static_library_native_artifacts_to_file = Native artifacts to link against have been written to {$path}. The order and any duplication can be significant on some platforms. + codegen_ssa_stripping_debug_info_failed = stripping debug info with `{$util}` failed: {$status} .note = {$output} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 0dfb41f42f0..cd6201648ee 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -12,8 +12,8 @@ use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; -use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip}; -use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind}; +use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, OutFileName, Strip}; +use rustc_session::config::{OutputFilenames, OutputType, PrintKind, SplitDwarfKind}; use rustc_session::cstore::DllImport; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; @@ -596,8 +596,10 @@ fn link_staticlib<'a>( all_native_libs.extend_from_slice(&codegen_results.crate_info.used_libraries); - if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { - print_native_static_libs(sess, &all_native_libs, &all_rust_dylibs); + for print in &sess.opts.prints { + if print.kind == PrintKind::NativeStaticLibs { + print_native_static_libs(sess, &print.out, &all_native_libs, &all_rust_dylibs); + } } Ok(()) @@ -744,8 +746,11 @@ fn link_natively<'a>( cmd.env_remove(k.as_ref()); } - if sess.opts.prints.contains(&PrintRequest::LinkArgs) { - println!("{:?}", &cmd); + for print in &sess.opts.prints { + if print.kind == PrintKind::LinkArgs { + let content = format!("{cmd:?}"); + print.out.overwrite(&content, sess); + } } // May have not found libraries in the right formats. @@ -1231,22 +1236,21 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { } } - let channel = option_env!("CFG_RELEASE_CHANNEL") - .map(|channel| format!("-{}", channel)) - .unwrap_or_default(); + let channel = + option_env!("CFG_RELEASE_CHANNEL").map(|channel| format!("-{channel}")).unwrap_or_default(); if sess.target.is_like_osx { // On Apple platforms, the sanitizer is always built as a dylib, and // LLVM will link to `@rpath/*.dylib`, so we need to specify an // rpath to the library as well (the rpath should be absolute, see // PR #41352 for details). - let filename = format!("rustc{}_rt.{}", channel, name); + let filename = format!("rustc{channel}_rt.{name}"); let path = find_sanitizer_runtime(&sess, &filename); let rpath = path.to_str().expect("non-utf8 component in path"); linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); linker.link_dylib(&filename, false, true); } else { - let filename = format!("librustc{}_rt.{}.a", channel, name); + let filename = format!("librustc{channel}_rt.{name}.a"); let path = find_sanitizer_runtime(&sess, &filename).join(&filename); linker.link_whole_rlib(&path); } @@ -1386,6 +1390,7 @@ enum RlibFlavor { fn print_native_static_libs( sess: &Session, + out: &OutFileName, all_native_libs: &[NativeLib], all_rust_dylibs: &[&Path], ) { @@ -1409,12 +1414,12 @@ fn print_native_static_libs( } else if sess.target.linker_flavor.is_gnu() { Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name)) } else { - Some(format!("-l{}", name)) + Some(format!("-l{name}")) } } NativeLibKind::Framework { .. } => { // ld-only syntax, since there are no frameworks in MSVC - Some(format!("-framework {}", name)) + Some(format!("-framework {name}")) } // These are included, no need to print them NativeLibKind::Static { bundle: None | Some(true), .. } @@ -1451,19 +1456,30 @@ fn print_native_static_libs( // `foo.lib` file if the dll doesn't actually export any symbols, so we // check to see if the file is there and just omit linking to it if it's // not present. - let name = format!("{}.dll.lib", lib); + let name = format!("{lib}.dll.lib"); if path.join(&name).exists() { lib_args.push(name); } } else { - lib_args.push(format!("-l{}", lib)); + lib_args.push(format!("-l{lib}")); } } - if !lib_args.is_empty() { - sess.emit_note(errors::StaticLibraryNativeArtifacts); - // Prefix for greppability - // Note: This must not be translated as tools are allowed to depend on this exact string. - sess.note_without_error(format!("native-static-libs: {}", &lib_args.join(" "))); + + match out { + OutFileName::Real(path) => { + out.overwrite(&lib_args.join(" "), sess); + if !lib_args.is_empty() { + sess.emit_note(errors::StaticLibraryNativeArtifactsToFile { path }); + } + } + OutFileName::Stdout => { + if !lib_args.is_empty() { + sess.emit_note(errors::StaticLibraryNativeArtifacts); + // Prefix for greppability + // Note: This must not be translated as tools are allowed to depend on this exact string. + sess.note_without_error(format!("native-static-libs: {}", &lib_args.join(" "))); + } + } } } @@ -1611,8 +1627,8 @@ fn exec_linker( write!(f, "\"")?; for c in self.arg.chars() { match c { - '"' => write!(f, "\\{}", c)?, - c => write!(f, "{}", c)?, + '"' => write!(f, "\\{c}")?, + c => write!(f, "{c}")?, } } write!(f, "\"")?; @@ -1629,8 +1645,8 @@ fn exec_linker( // ensure the line is interpreted as one whole argument. for c in self.arg.chars() { match c { - '\\' | ' ' => write!(f, "\\{}", c)?, - c => write!(f, "{}", c)?, + '\\' | ' ' => write!(f, "\\{c}")?, + c => write!(f, "{c}")?, } } } @@ -2267,7 +2283,7 @@ fn add_order_independent_options( } else { "" }; - cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix)); + cmd.arg(format!("--dynamic-linker={prefix}ld.so.1")); } if sess.target.eh_frame_header { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index cd56f85cccd..4c04fc60b98 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -309,7 +309,7 @@ impl<'a> GccLinker<'a> { self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display())); }; self.linker_args(&[ - &format!("-plugin-opt={}", opt_level), + &format!("-plugin-opt={opt_level}"), &format!("-plugin-opt=mcpu={}", self.target_cpu), ]); } @@ -487,7 +487,7 @@ impl<'a> Linker for GccLinker<'a> { fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { self.hint_dynamic(); - self.cmd.arg(format!("-l{}", lib)); + self.cmd.arg(format!("-l{lib}")); } fn link_framework(&mut self, framework: &str, as_needed: bool) { @@ -671,8 +671,8 @@ impl<'a> Linker for GccLinker<'a> { let res: io::Result<()> = try { let mut f = BufWriter::new(File::create(&path)?); for sym in symbols { - debug!(" _{}", sym); - writeln!(f, "_{}", sym)?; + debug!(" _{sym}"); + writeln!(f, "_{sym}")?; } }; if let Err(error) = res { @@ -686,8 +686,8 @@ impl<'a> Linker for GccLinker<'a> { // because LD doesn't like when it's empty writeln!(f, "EXPORTS")?; for symbol in symbols { - debug!(" _{}", symbol); - writeln!(f, " {}", symbol)?; + debug!(" _{symbol}"); + writeln!(f, " {symbol}")?; } }; if let Err(error) = res { @@ -701,8 +701,8 @@ impl<'a> Linker for GccLinker<'a> { if !symbols.is_empty() { writeln!(f, " global:")?; for sym in symbols { - debug!(" {};", sym); - writeln!(f, " {};", sym)?; + debug!(" {sym};"); + writeln!(f, " {sym};")?; } } writeln!(f, "\n local:\n *;\n}};")?; @@ -837,7 +837,7 @@ impl<'a> Linker for MsvcLinker<'a> { // `foo.lib` file if the dll doesn't actually export any symbols, so we // check to see if the file is there and just omit linking to it if it's // not present. - let name = format!("{}.dll.lib", lib); + let name = format!("{lib}.dll.lib"); if path.join(&name).exists() { self.cmd.arg(name); } @@ -977,8 +977,8 @@ impl<'a> Linker for MsvcLinker<'a> { writeln!(f, "LIBRARY")?; writeln!(f, "EXPORTS")?; for symbol in symbols { - debug!(" _{}", symbol); - writeln!(f, " {}", symbol)?; + debug!(" _{symbol}"); + writeln!(f, " {symbol}")?; } }; if let Err(error) = res { @@ -992,7 +992,7 @@ impl<'a> Linker for MsvcLinker<'a> { fn subsystem(&mut self, subsystem: &str) { // Note that previous passes of the compiler validated this subsystem, // so we just blindly pass it to the linker. - self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem)); + self.cmd.arg(&format!("/SUBSYSTEM:{subsystem}")); // Windows has two subsystems we're interested in right now, the console // and windows subsystems. These both implicitly have different entry @@ -1147,7 +1147,7 @@ impl<'a> Linker for EmLinker<'a> { &symbols.iter().map(|sym| "_".to_owned() + sym).collect::<Vec<_>>(), ) .unwrap(); - debug!("{}", encoded); + debug!("{encoded}"); arg.push(encoded); @@ -1350,7 +1350,7 @@ impl<'a> Linker for L4Bender<'a> { } fn link_staticlib(&mut self, lib: &str, _verbatim: bool) { self.hint_static(); - self.cmd.arg(format!("-PC{}", lib)); + self.cmd.arg(format!("-PC{lib}")); } fn link_rlib(&mut self, lib: &Path) { self.hint_static(); @@ -1399,7 +1399,7 @@ impl<'a> Linker for L4Bender<'a> { fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { self.hint_static(); - self.cmd.arg("--whole-archive").arg(format!("-l{}", lib)); + self.cmd.arg("--whole-archive").arg(format!("-l{lib}")); self.cmd.arg("--no-whole-archive"); } @@ -1453,7 +1453,7 @@ impl<'a> Linker for L4Bender<'a> { } fn subsystem(&mut self, subsystem: &str) { - self.cmd.arg(&format!("--subsystem {}", subsystem)); + self.cmd.arg(&format!("--subsystem {subsystem}")); } fn reset_per_library_state(&mut self) { @@ -1518,12 +1518,12 @@ impl<'a> AixLinker<'a> { impl<'a> Linker for AixLinker<'a> { fn link_dylib(&mut self, lib: &str, _verbatim: bool, _as_needed: bool) { self.hint_dynamic(); - self.cmd.arg(format!("-l{}", lib)); + self.cmd.arg(format!("-l{lib}")); } fn link_staticlib(&mut self, lib: &str, _verbatim: bool) { self.hint_static(); - self.cmd.arg(format!("-l{}", lib)); + self.cmd.arg(format!("-l{lib}")); } fn link_rlib(&mut self, lib: &Path) { @@ -1573,7 +1573,7 @@ impl<'a> Linker for AixLinker<'a> { fn link_rust_dylib(&mut self, lib: &str, _: &Path) { self.hint_dynamic(); - self.cmd.arg(format!("-l{}", lib)); + self.cmd.arg(format!("-l{lib}")); } fn link_framework(&mut self, _framework: &str, _as_needed: bool) { @@ -1626,12 +1626,12 @@ impl<'a> Linker for AixLinker<'a> { let mut f = BufWriter::new(File::create(&path)?); // FIXME: use llvm-nm to generate export list. for symbol in symbols { - debug!(" _{}", symbol); - writeln!(f, " {}", symbol)?; + debug!(" _{symbol}"); + writeln!(f, " {symbol}")?; } }; if let Err(e) = res { - self.sess.fatal(format!("failed to write export file: {}", e)); + self.sess.fatal(format!("failed to write export file: {e}")); } self.cmd.arg(format!("-bE:{}", path.to_str().unwrap())); } @@ -1908,7 +1908,7 @@ impl<'a> Linker for BpfLinker<'a> { let res: io::Result<()> = try { let mut f = BufWriter::new(File::create(&path)?); for sym in symbols { - writeln!(f, "{}", sym)?; + writeln!(f, "{sym}")?; } }; if let Err(error) = res { diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 1f827a2375d..5c7df29444b 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -10,8 +10,6 @@ use object::{ ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; -use snap::write::FrameEncoder; - use rustc_data_structures::memmap::Mmap; use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice}; use rustc_metadata::fs::METADATA_FILENAME; @@ -160,20 +158,19 @@ pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a { let offset = metadata_symbol.address() as usize; if offset < 4 { - return Err(format!("Invalid metadata symbol offset: {}", offset)); + return Err(format!("Invalid metadata symbol offset: {offset}")); } // The offset specifies the location of rustc metadata in the comment section. // The metadata is preceded by a 4-byte length field. let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize; if offset + len > (info_data.len() as usize) { return Err(format!( - "Metadata at offset {} with size {} is beyond .info section", - offset, len + "Metadata at offset {offset} with size {len} is beyond .info section" )); } return Ok(&info_data[offset..(offset + len)]); } else { - return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME)); + return Err(format!("Unable to find symbol {AIX_METADATA_SYMBOL_NAME}")); }; } @@ -193,8 +190,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static } "x86" => Architecture::I386, "s390x" => Architecture::S390x, - "mips" => Architecture::Mips, - "mips64" => Architecture::Mips64, + "mips" | "mips32r6" => Architecture::Mips, + "mips64" | "mips64r6" => Architecture::Mips64, "x86_64" => { if sess.target.pointer_width == 32 { Architecture::X86_64_X32 @@ -481,19 +478,15 @@ pub fn create_compressed_metadata_file( metadata: &EncodedMetadata, symbol_name: &str, ) -> Vec<u8> { - let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); - // Our length will be backfilled once we're done writing - compressed.write_all(&[0; 4]).unwrap(); - FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap(); - let meta_len = rustc_metadata::METADATA_HEADER.len(); - let data_len = (compressed.len() - meta_len - 4) as u32; - compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes()); + let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec(); + packed_metadata.write_all(&(metadata.raw_data().len() as u32).to_be_bytes()).unwrap(); + packed_metadata.extend(metadata.raw_data()); let Some(mut file) = create_object_file(sess) else { - return compressed.to_vec(); + return packed_metadata.to_vec(); }; if file.format() == BinaryFormat::Xcoff { - return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name); + return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name); } let section = file.add_section( file.segment_name(StandardSegment::Data).to_vec(), @@ -507,14 +500,14 @@ pub fn create_compressed_metadata_file( } _ => {} }; - let offset = file.append_section_data(section, &compressed, 1); + let offset = file.append_section_data(section, &packed_metadata, 1); // For MachO and probably PE this is necessary to prevent the linker from throwing away the // .rustc section. For ELF this isn't necessary, but it also doesn't harm. file.add_symbol(Symbol { name: symbol_name.as_bytes().to_vec(), value: offset, - size: compressed.len() as u64, + size: packed_metadata.len() as u64, kind: SymbolKind::Data, scope: SymbolScope::Dynamic, weak: false, diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 0b5656c9ad1..18268622341 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -86,7 +86,7 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> Str output.pop(); // strip filename let output = fs::canonicalize(&output).unwrap_or(output); let relative = path_relative_from(&lib, &output) - .unwrap_or_else(|| panic!("couldn't create relative path from {:?} to {:?}", output, lib)); + .unwrap_or_else(|| panic!("couldn't create relative path from {output:?} to {lib:?}")); // FIXME (#9639): This needs to handle non-utf8 paths format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path")) } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index ececa29b231..40718525741 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -349,8 +349,6 @@ pub struct CodegenContext<B: WriteBackendMethods> { /// Directory into which should the LLVM optimization remarks be written. /// If `None`, they will be written to stderr. pub remark_dir: Option<PathBuf>, - /// Worker thread number - pub worker: usize, /// The incremental compilation session directory, or None if we are not /// compiling incrementally pub incr_comp_session_dir: Option<PathBuf>, @@ -362,7 +360,7 @@ pub struct CodegenContext<B: WriteBackendMethods> { impl<B: WriteBackendMethods> CodegenContext<B> { pub fn create_diag_handler(&self) -> Handler { - Handler::with_emitter(true, None, Box::new(self.diag_emitter.clone())) + Handler::with_emitter(Box::new(self.diag_emitter.clone())) } pub fn config(&self, kind: ModuleKind) -> &ModuleConfig { @@ -376,38 +374,39 @@ impl<B: WriteBackendMethods> CodegenContext<B> { fn generate_lto_work<B: ExtraBackendMethods>( cgcx: &CodegenContext<B>, - needs_fat_lto: Vec<FatLTOInput<B>>, + needs_fat_lto: Vec<FatLtoInput<B>>, needs_thin_lto: Vec<(String, B::ThinBuffer)>, import_only_modules: Vec<(SerializedModule<B::ModuleBuffer>, WorkProduct)>, ) -> Vec<(WorkItem<B>, u64)> { let _prof_timer = cgcx.prof.generic_activity("codegen_generate_lto_work"); - let (lto_modules, copy_jobs) = if !needs_fat_lto.is_empty() { + if !needs_fat_lto.is_empty() { assert!(needs_thin_lto.is_empty()); - let lto_module = + let module = B::run_fat_lto(cgcx, needs_fat_lto, import_only_modules).unwrap_or_else(|e| e.raise()); - (vec![lto_module], vec![]) + // We are adding a single work item, so the cost doesn't matter. + vec![(WorkItem::LTO(module), 0)] } else { assert!(needs_fat_lto.is_empty()); - B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules).unwrap_or_else(|e| e.raise()) - }; - - lto_modules - .into_iter() - .map(|module| { - let cost = module.cost(); - (WorkItem::LTO(module), cost) - }) - .chain(copy_jobs.into_iter().map(|wp| { - ( - WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { - name: wp.cgu_name.clone(), - source: wp, - }), - 0, - ) - })) - .collect() + let (lto_modules, copy_jobs) = B::run_thin_lto(cgcx, needs_thin_lto, import_only_modules) + .unwrap_or_else(|e| e.raise()); + lto_modules + .into_iter() + .map(|module| { + let cost = module.cost(); + (WorkItem::LTO(module), cost) + }) + .chain(copy_jobs.into_iter().map(|wp| { + ( + WorkItem::CopyPostLtoArtifacts(CachedModuleCodegen { + name: wp.cgu_name.clone(), + source: wp, + }), + 0, // copying is very cheap + ) + })) + .collect() + } } pub struct CompiledModules { @@ -709,7 +708,7 @@ impl<B: WriteBackendMethods> WorkItem<B> { fn desc(short: &str, _long: &str, name: &str) -> String { // The short label is three bytes, and is followed by a space. That // leaves 11 bytes for the CGU name. How we obtain those 11 bytes - // depends on the the CGU name form. + // depends on the CGU name form. // // - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part // before the `-cgu.0` is the same for every CGU, so use the @@ -742,22 +741,32 @@ impl<B: WriteBackendMethods> WorkItem<B> { } match self { - WorkItem::Optimize(m) => desc("opt", "optimize module {}", &m.name), - WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for {}", &m.name), - WorkItem::LTO(m) => desc("lto", "LTO module {}", m.name()), + WorkItem::Optimize(m) => desc("opt", "optimize module", &m.name), + WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for", &m.name), + WorkItem::LTO(m) => desc("lto", "LTO module", m.name()), } } } /// A result produced by the backend. pub(crate) enum WorkItemResult<B: WriteBackendMethods> { - Compiled(CompiledModule), + /// The backend has finished compiling a CGU, nothing more required. + Finished(CompiledModule), + + /// The backend has finished compiling a CGU, which now needs linking + /// because `-Zcombine-cgu` was specified. NeedsLink(ModuleCodegen<B::Module>), - NeedsFatLTO(FatLTOInput<B>), - NeedsThinLTO(String, B::ThinBuffer), + + /// The backend has finished compiling a CGU, which now needs to go through + /// fat LTO. + NeedsFatLto(FatLtoInput<B>), + + /// The backend has finished compiling a CGU, which now needs to go through + /// thin LTO. + NeedsThinLto(String, B::ThinBuffer), } -pub enum FatLTOInput<B: WriteBackendMethods> { +pub enum FatLtoInput<B: WriteBackendMethods> { Serialized { name: String, buffer: B::ModuleBuffer }, InMemory(ModuleCodegen<B::Module>), } @@ -846,7 +855,7 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); } - Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) + Ok(WorkItemResult::NeedsThinLto(name, thin_buffer)) } ComputedLtoType::Fat => match bitcode { Some(path) => { @@ -854,9 +863,9 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>( fs::write(&path, buffer.data()).unwrap_or_else(|e| { panic!("Error writing pre-lto-bitcode file `{}`: {}", path.display(), e); }); - Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::Serialized { name, buffer })) + Ok(WorkItemResult::NeedsFatLto(FatLtoInput::Serialized { name, buffer })) } - None => Ok(WorkItemResult::NeedsFatLTO(FatLTOInput::InMemory(module))), + None => Ok(WorkItemResult::NeedsFatLto(FatLtoInput::InMemory(module))), }, } } @@ -906,7 +915,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( load_from_incr_comp_dir(dwarf_obj_out, &saved_dwarf_object_file) }); - WorkItemResult::Compiled(CompiledModule { + WorkItemResult::Finished(CompiledModule { name: module.name, kind: ModuleKind::Regular, object, @@ -936,7 +945,7 @@ fn finish_intra_module_work<B: ExtraBackendMethods>( || module.kind == ModuleKind::Allocator { let module = unsafe { B::codegen(cgcx, &diag_handler, module, module_config)? }; - Ok(WorkItemResult::Compiled(module)) + Ok(WorkItemResult::Finished(module)) } else { Ok(WorkItemResult::NeedsLink(module)) } @@ -987,10 +996,15 @@ struct Diagnostic { } #[derive(PartialEq, Clone, Copy, Debug)] -enum MainThreadWorkerState { +enum MainThreadState { + /// Doing nothing. Idle, + + /// Doing codegen, i.e. MIR-to-LLVM-IR conversion. Codegenning, - LLVMing, + + /// Idle, but lending the compiler process's Token to an LLVM thread so it can do useful work. + Lending, } fn start_executing_work<B: ExtraBackendMethods>( @@ -1089,7 +1103,6 @@ fn start_executing_work<B: ExtraBackendMethods>( exported_symbols, remark: sess.opts.cg.remark.clone(), remark_dir, - worker: 0, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(), coordinator_send, @@ -1242,7 +1255,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // Each LLVM module is automatically sent back to the coordinator for LTO if // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. - return B::spawn_thread(cgcx.time_trace, move || { + return B::spawn_named_thread(cgcx.time_trace, "coordinator".to_string(), move || { let mut worker_id_counter = 0; let mut free_worker_ids = Vec::new(); let mut get_worker_id = |free_worker_ids: &mut Vec<usize>| { @@ -1285,10 +1298,19 @@ fn start_executing_work<B: ExtraBackendMethods>( // the implicit Token the compiler process owns no matter what. let mut tokens = Vec::new(); - let mut main_thread_worker_state = MainThreadWorkerState::Idle; - let mut running = 0; + let mut main_thread_state = MainThreadState::Idle; + + // How many LLVM worker threads are running while holding a Token. This + // *excludes* any that the main thread is lending a Token to. + let mut running_with_own_token = 0; + + // How many LLVM worker threads are running in total. This *includes* + // any that the main thread is lending a Token to. + let running_with_any_token = |main_thread_state, running_with_own_token| { + running_with_own_token + + if main_thread_state == MainThreadState::Lending { 1 } else { 0 } + }; - let prof = &cgcx.prof; let mut llvm_start_time: Option<VerboseTimingGuard<'_>> = None; // Run the message loop while there's still anything that needs message @@ -1296,66 +1318,62 @@ fn start_executing_work<B: ExtraBackendMethods>( // wait for all existing work to finish, so many of the conditions here // only apply if codegen hasn't been aborted as they represent pending // work to be done. - while codegen_state == Ongoing - || running > 0 - || main_thread_worker_state == MainThreadWorkerState::LLVMing - || (codegen_state == Completed - && !(work_items.is_empty() - && needs_fat_lto.is_empty() - && needs_thin_lto.is_empty() - && lto_import_only_modules.is_empty() - && main_thread_worker_state == MainThreadWorkerState::Idle)) - { + loop { // While there are still CGUs to be codegened, the coordinator has // to decide how to utilize the compiler processes implicit Token: // For codegenning more CGU or for running them through LLVM. if codegen_state == Ongoing { - if main_thread_worker_state == MainThreadWorkerState::Idle { + if main_thread_state == MainThreadState::Idle { // Compute the number of workers that will be running once we've taken as many // items from the work queue as we can, plus one for the main thread. It's not - // critically important that we use this instead of just `running`, but it - // prevents the `queue_full_enough` heuristic from fluctuating just because a - // worker finished up and we decreased the `running` count, even though we're - // just going to increase it right after this when we put a new worker to work. - let extra_tokens = tokens.len().checked_sub(running).unwrap(); + // critically important that we use this instead of just + // `running_with_own_token`, but it prevents the `queue_full_enough` heuristic + // from fluctuating just because a worker finished up and we decreased the + // `running_with_own_token` count, even though we're just going to increase it + // right after this when we put a new worker to work. + let extra_tokens = tokens.len().checked_sub(running_with_own_token).unwrap(); let additional_running = std::cmp::min(extra_tokens, work_items.len()); - let anticipated_running = running + additional_running + 1; + let anticipated_running = running_with_own_token + additional_running + 1; if !queue_full_enough(work_items.len(), anticipated_running) { // The queue is not full enough, process more codegen units: if codegen_worker_send.send(CguMessage).is_err() { panic!("Could not send CguMessage to main thread") } - main_thread_worker_state = MainThreadWorkerState::Codegenning; + main_thread_state = MainThreadState::Codegenning; } else { // The queue is full enough to not let the worker // threads starve. Use the implicit Token to do some // LLVM work too. let (item, _) = work_items.pop().expect("queue empty - queue_full_enough() broken?"); - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - ..cgcx.clone() - }; - maybe_start_llvm_timer( - prof, - cgcx.config(item.module_kind()), + main_thread_state = MainThreadState::Lending; + spawn_work( + &cgcx, &mut llvm_start_time, + get_worker_id(&mut free_worker_ids), + item, ); - main_thread_worker_state = MainThreadWorkerState::LLVMing; - spawn_work(cgcx, item); } } } else if codegen_state == Completed { - // If we've finished everything related to normal codegen - // then it must be the case that we've got some LTO work to do. - // Perform the serial work here of figuring out what we're - // going to LTO and then push a bunch of work items onto our - // queue to do LTO - if work_items.is_empty() - && running == 0 - && main_thread_worker_state == MainThreadWorkerState::Idle + if running_with_any_token(main_thread_state, running_with_own_token) == 0 + && work_items.is_empty() { + // All codegen work is done. Do we have LTO work to do? + if needs_fat_lto.is_empty() + && needs_thin_lto.is_empty() + && lto_import_only_modules.is_empty() + { + // Nothing more to do! + break; + } + + // We have LTO work to do. Perform the serial work here of + // figuring out what we're going to LTO and then push a + // bunch of work items onto our queue to do LTO. This all + // happens on the coordinator thread but it's very quick so + // we don't worry about tokens. assert!(!started_lto); started_lto = true; @@ -1379,20 +1397,16 @@ fn start_executing_work<B: ExtraBackendMethods>( // In this branch, we know that everything has been codegened, // so it's just a matter of determining whether the implicit // Token is free to use for LLVM work. - match main_thread_worker_state { - MainThreadWorkerState::Idle => { + match main_thread_state { + MainThreadState::Idle => { if let Some((item, _)) = work_items.pop() { - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - ..cgcx.clone() - }; - maybe_start_llvm_timer( - prof, - cgcx.config(item.module_kind()), + main_thread_state = MainThreadState::Lending; + spawn_work( + &cgcx, &mut llvm_start_time, + get_worker_id(&mut free_worker_ids), + item, ); - main_thread_worker_state = MainThreadWorkerState::LLVMing; - spawn_work(cgcx, item); } else { // There is no unstarted work, so let the main thread // take over for a running worker. Otherwise the @@ -1400,16 +1414,16 @@ fn start_executing_work<B: ExtraBackendMethods>( // We reduce the `running` counter by one. The // `tokens.truncate()` below will take care of // giving the Token back. - debug_assert!(running > 0); - running -= 1; - main_thread_worker_state = MainThreadWorkerState::LLVMing; + debug_assert!(running_with_own_token > 0); + running_with_own_token -= 1; + main_thread_state = MainThreadState::Lending; } } - MainThreadWorkerState::Codegenning => bug!( + MainThreadState::Codegenning => bug!( "codegen worker should not be codegenning after \ codegen was already completed" ), - MainThreadWorkerState::LLVMing => { + MainThreadState::Lending => { // Already making good use of that token } } @@ -1417,35 +1431,39 @@ fn start_executing_work<B: ExtraBackendMethods>( // Don't queue up any more work if codegen was aborted, we're // just waiting for our existing children to finish. assert!(codegen_state == Aborted); + if running_with_any_token(main_thread_state, running_with_own_token) == 0 { + break; + } } // Spin up what work we can, only doing this while we've got available // parallelism slots and work left to spawn. - while codegen_state != Aborted && !work_items.is_empty() && running < tokens.len() { - let (item, _) = work_items.pop().unwrap(); - - maybe_start_llvm_timer(prof, cgcx.config(item.module_kind()), &mut llvm_start_time); - - let cgcx = - CodegenContext { worker: get_worker_id(&mut free_worker_ids), ..cgcx.clone() }; - - spawn_work(cgcx, item); - running += 1; + if codegen_state != Aborted { + while !work_items.is_empty() && running_with_own_token < tokens.len() { + let (item, _) = work_items.pop().unwrap(); + spawn_work( + &cgcx, + &mut llvm_start_time, + get_worker_id(&mut free_worker_ids), + item, + ); + running_with_own_token += 1; + } } - // Relinquish accidentally acquired extra tokens - tokens.truncate(running); + // Relinquish accidentally acquired extra tokens. + tokens.truncate(running_with_own_token); // If a thread exits successfully then we drop a token associated - // with that worker and update our `running` count. We may later - // re-acquire a token to continue running more work. We may also not - // actually drop a token here if the worker was running with an - // "ephemeral token" + // with that worker and update our `running_with_own_token` count. + // We may later re-acquire a token to continue running more work. + // We may also not actually drop a token here if the worker was + // running with an "ephemeral token". let mut free_worker = |worker_id| { - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - main_thread_worker_state = MainThreadWorkerState::Idle; + if main_thread_state == MainThreadState::Lending { + main_thread_state = MainThreadState::Idle; } else { - running -= 1; + running_with_own_token -= 1; } free_worker_ids.push(worker_id); @@ -1461,17 +1479,17 @@ fn start_executing_work<B: ExtraBackendMethods>( Ok(token) => { tokens.push(token); - if main_thread_worker_state == MainThreadWorkerState::LLVMing { + if main_thread_state == MainThreadState::Lending { // If the main thread token is used for LLVM work // at the moment, we turn that thread into a regular // LLVM worker thread, so the main thread is free // to react to codegen demand. - main_thread_worker_state = MainThreadWorkerState::Idle; - running += 1; + main_thread_state = MainThreadState::Idle; + running_with_own_token += 1; } } Err(e) => { - let msg = &format!("failed to acquire jobserver token: {}", e); + let msg = &format!("failed to acquire jobserver token: {e}"); shared_emitter.fatal(msg); codegen_state = Aborted; } @@ -1496,16 +1514,16 @@ fn start_executing_work<B: ExtraBackendMethods>( if !cgcx.opts.unstable_opts.no_parallel_llvm { helper.request_token(); } - assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); - main_thread_worker_state = MainThreadWorkerState::Idle; + assert_eq!(main_thread_state, MainThreadState::Codegenning); + main_thread_state = MainThreadState::Idle; } Message::CodegenComplete => { if codegen_state != Aborted { codegen_state = Completed; } - assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); - main_thread_worker_state = MainThreadWorkerState::Idle; + assert_eq!(main_thread_state, MainThreadState::Codegenning); + main_thread_state = MainThreadState::Idle; } // If codegen is aborted that means translation was aborted due @@ -1513,7 +1531,8 @@ fn start_executing_work<B: ExtraBackendMethods>( // to exit as soon as possible, but we want to make sure all // existing work has finished. Flag codegen as being done, and // then conditions above will ensure no more work is spawned but - // we'll keep executing this loop until `running` hits 0. + // we'll keep executing this loop until `running_with_own_token` + // hits 0. Message::CodegenAborted => { codegen_state = Aborted; } @@ -1522,9 +1541,10 @@ fn start_executing_work<B: ExtraBackendMethods>( free_worker(worker_id); match result { - Ok(WorkItemResult::Compiled(compiled_module)) => { + Ok(WorkItemResult::Finished(compiled_module)) => { match compiled_module.kind { ModuleKind::Regular => { + assert!(needs_link.is_empty()); compiled_modules.push(compiled_module); } ModuleKind::Allocator => { @@ -1535,14 +1555,17 @@ fn start_executing_work<B: ExtraBackendMethods>( } } Ok(WorkItemResult::NeedsLink(module)) => { + assert!(compiled_modules.is_empty()); needs_link.push(module); } - Ok(WorkItemResult::NeedsFatLTO(fat_lto_input)) => { + Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => { assert!(!started_lto); + assert!(needs_thin_lto.is_empty()); needs_fat_lto.push(fat_lto_input); } - Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) => { + Ok(WorkItemResult::NeedsThinLto(name, thin_buffer)) => { assert!(!started_lto); + assert!(needs_fat_lto.is_empty()); needs_thin_lto.push((name, thin_buffer)); } Err(Some(WorkerFatalError)) => { @@ -1560,9 +1583,9 @@ fn start_executing_work<B: ExtraBackendMethods>( Message::AddImportOnlyModule { module_data, work_product } => { assert!(!started_lto); assert_eq!(codegen_state, Ongoing); - assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning); + assert_eq!(main_thread_state, MainThreadState::Codegenning); lto_import_only_modules.push((module_data, work_product)); - main_thread_worker_state = MainThreadWorkerState::Idle; + main_thread_state = MainThreadState::Idle; } } } @@ -1595,7 +1618,8 @@ fn start_executing_work<B: ExtraBackendMethods>( modules: compiled_modules, allocator_module: compiled_allocator_module, }) - }); + }) + .expect("failed to spawn coordinator thread"); // A heuristic that determines if we have enough LLVM WorkItems in the // queue so that the main thread can do LLVM work instead of codegen @@ -1653,23 +1677,24 @@ fn start_executing_work<B: ExtraBackendMethods>( let quarter_of_workers = workers_running - 3 * workers_running / 4; items_in_queue > 0 && items_in_queue >= quarter_of_workers } - - fn maybe_start_llvm_timer<'a>( - prof: &'a SelfProfilerRef, - config: &ModuleConfig, - llvm_start_time: &mut Option<VerboseTimingGuard<'a>>, - ) { - if config.time_module && llvm_start_time.is_none() { - *llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes")); - } - } } /// `FatalError` is explicitly not `Send`. #[must_use] pub struct WorkerFatalError; -fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>) { +fn spawn_work<'a, B: ExtraBackendMethods>( + cgcx: &'a CodegenContext<B>, + llvm_start_time: &mut Option<VerboseTimingGuard<'a>>, + worker_id: usize, + work: WorkItem<B>, +) { + if cgcx.config(work.module_kind()).time_module && llvm_start_time.is_none() { + *llvm_start_time = Some(cgcx.prof.verbose_generic_activity("LLVM_passes")); + } + + let cgcx = cgcx.clone(); + B::spawn_named_thread(cgcx.time_trace, work.short_description(), move || { // Set up a destructor which will fire off a message that we're done as // we exit. @@ -1692,11 +1717,8 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B> } } - let mut bomb = Bomb::<B> { - coordinator_send: cgcx.coordinator_send.clone(), - result: None, - worker_id: cgcx.worker, - }; + let mut bomb = + Bomb::<B> { coordinator_send: cgcx.coordinator_send.clone(), result: None, worker_id }; // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. @@ -1728,7 +1750,7 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B> }) }; }) - .expect("failed to spawn thread"); + .expect("failed to spawn work thread"); } enum SharedEmitterMessage { @@ -1945,6 +1967,10 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { self.backend.print_pass_timings() } + if sess.print_llvm_stats() { + self.backend.print_statistics() + } + ( CodegenResults { metadata: self.metadata, @@ -1958,19 +1984,6 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { ) } - pub fn submit_pre_codegened_module_to_llvm( - &self, - tcx: TyCtxt<'_>, - module: ModuleCodegen<B::Module>, - ) { - self.wait_for_signal_to_codegen_item(); - self.check_for_errors(tcx.sess); - - // These are generally cheap and won't throw off scheduling. - let cost = 0; - submit_codegened_module_to_llvm(&self.backend, &self.coordinator.sender, module, cost); - } - pub fn codegen_finished(&self, tcx: TyCtxt<'_>) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); @@ -2036,8 +2049,8 @@ pub fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>( }))); } -pub fn pre_lto_bitcode_filename(module_name: &str) -> String { - format!("{}.{}", module_name, PRE_LTO_BC_EXT) +fn pre_lto_bitcode_filename(module_name: &str) -> String { + format!("{module_name}.{PRE_LTO_BC_EXT}") } fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index dc862803274..4819ab22723 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -38,6 +38,7 @@ use rustc_span::symbol::sym; use rustc_span::Symbol; use rustc_target::abi::{Align, FIRST_VARIANT}; +use std::cmp; use std::collections::BTreeSet; use std::time::{Duration, Instant}; @@ -164,50 +165,27 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { - let ptr_ty = cx.type_i8p(); + let ptr_ty = cx.type_ptr(); let ptr_align = cx.tcx().data_layout.pointer_align.abi; - let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind); - let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty)); let gep = bx.inbounds_gep( ptr_ty, - llvtable, + old_info, &[bx.const_usize(u64::try_from(entry_idx).unwrap())], ); let new_vptr = bx.load(ptr_ty, gep, ptr_align); bx.nonnull_metadata(new_vptr); // VTable loads are invariant. bx.set_invariant_load(new_vptr); - bx.pointercast(new_vptr, vtable_ptr_ty) + new_vptr } else { old_info } } - (_, &ty::Dynamic(ref data, _, target_dyn_kind)) => { - let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind); - cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty) - } + (_, &ty::Dynamic(ref data, _, _)) => meth::get_vtable(cx, source, data.principal()), _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), } } -// Returns the vtable pointer type of a `dyn` or `dyn*` type -fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>( - cx: &Cx, - target: Ty<'tcx>, - kind: ty::DynKind, -) -> <Cx as BackendTypes>::Type { - cx.scalar_pair_element_backend_type( - cx.layout_of(match kind { - // vtable is the second field of `*mut dyn Trait` - ty::Dyn => Ty::new_mut_ptr(cx.tcx(), target), - // vtable is the second field of `dyn* Trait` - ty::DynStar => target, - }), - 1, - true, - ) -} - /// Coerces `src` to `dst_ty`. `src_ty` must be a pointer. pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, @@ -221,8 +199,7 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { assert_eq!(bx.cx().type_is_sized(a), old_info.is_none()); - let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); - (bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info)) + (src, unsized_info(bx, a, b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); @@ -247,11 +224,7 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( assert_eq!(result, None); result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info)); } - let (lldata, llextra) = result.unwrap(); - let lldata_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true); - let llextra_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true); - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - (bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty)) + result.unwrap() } _ => bug!("unsize_ptr: called on bad types"), } @@ -270,11 +243,9 @@ pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)), "destination type must be a dyn*" ); - // FIXME(dyn-star): We can remove this when all supported LLVMs use opaque ptrs only. - let unit_ptr = bx.cx().type_ptr_to(bx.cx().type_struct(&[], false)); let src = match bx.cx().type_kind(bx.cx().backend_type(src_ty_and_layout)) { - TypeKind::Pointer => bx.pointercast(src, unit_ptr), - TypeKind::Integer => bx.inttoptr(src, unit_ptr), + TypeKind::Pointer => src, + TypeKind::Integer => bx.inttoptr(src, bx.type_ptr()), // FIXME(dyn-star): We probably have to do a bitcast first, then inttoptr. kind => bug!("unexpected TypeKind for left-hand side of `dyn*` cast: {kind:?}"), }; @@ -397,11 +368,6 @@ pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if flags == MemFlags::empty() && let Some(bty) = bx.cx().scalar_copy_backend_type(layout) { - // I look forward to only supporting opaque pointers - let pty = bx.type_ptr_to(bty); - let src = bx.pointercast(src, pty); - let dst = bx.pointercast(dst, pty); - let temp = bx.load(bty, src, src_align); bx.store(temp, dst, dst_align); } else { @@ -455,7 +421,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, // depending on whether the target needs `argc` and `argv` to be passed in. let llfty = if cx.sess().target.main_needs_argc_argv { - cx.type_func(&[cx.type_int(), cx.type_ptr_to(cx.type_i8p())], cx.type_int()) + cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int()) } else { cx.type_func(&[], cx.type_int()) }; @@ -489,7 +455,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.insert_reference_to_gdb_debug_scripts_section_global(); let isize_ty = cx.type_isize(); - let i8pp_ty = cx.type_ptr_to(cx.type_i8p()); + let ptr_ty = cx.type_ptr(); let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); let (start_fn, start_ty, args) = if let EntryFnType::Main { sigpipe } = entry_type { @@ -508,12 +474,11 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); - let start_ty = - cx.type_func(&[cx.val_ty(rust_main), isize_ty, i8pp_ty, i8_ty], isize_ty); + let start_ty = cx.type_func(&[cx.val_ty(rust_main), isize_ty, ptr_ty, i8_ty], isize_ty); (start_fn, start_ty, vec![rust_main, arg_argc, arg_argv, arg_sigpipe]) } else { debug!("using user-defined start fn"); - let start_ty = cx.type_func(&[isize_ty, i8pp_ty], isize_ty); + let start_ty = cx.type_func(&[isize_ty, ptr_ty], isize_ty); (rust_main, start_ty, vec![arg_argc, arg_argv]) }; @@ -540,7 +505,7 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } else { // The Rust start function doesn't need `argc` and `argv`, so just pass zeros. let arg_argc = bx.const_int(cx.type_int(), 0); - let arg_argv = bx.const_null(cx.type_ptr_to(cx.type_i8p())); + let arg_argv = bx.const_null(cx.type_ptr()); (arg_argc, arg_argv) } } @@ -663,9 +628,16 @@ pub fn codegen_crate<B: ExtraBackendMethods>( ) }); - ongoing_codegen.submit_pre_codegened_module_to_llvm( - tcx, + ongoing_codegen.wait_for_signal_to_codegen_item(); + ongoing_codegen.check_for_errors(tcx.sess); + + // These modules are generally cheap and won't throw off scheduling. + let cost = 0; + submit_codegened_module_to_llvm( + &backend, + &ongoing_codegen.coordinator.sender, ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }, + cost, ); } @@ -682,10 +654,10 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // are large size variations, this can reduce memory usage significantly. let codegen_units: Vec<_> = { let mut sorted_cgus = codegen_units.iter().collect::<Vec<_>>(); - sorted_cgus.sort_by_cached_key(|cgu| cgu.size_estimate()); + sorted_cgus.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate())); let (first_half, second_half) = sorted_cgus.split_at(sorted_cgus.len() / 2); - second_half.iter().rev().interleave(first_half).copied().collect() + first_half.iter().interleave(second_half.iter().rev()).copied().collect() }; // Calculate the CGU reuse @@ -760,7 +732,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( module, cost, ); - false } CguReuse::PreLto => { submit_pre_lto_module_to_llvm( @@ -772,7 +743,6 @@ pub fn codegen_crate<B: ExtraBackendMethods>( source: cgu.previous_work_product(tcx), }, ); - true } CguReuse::PostLto => { submit_post_lto_module_to_llvm( @@ -783,9 +753,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>( source: cgu.previous_work_product(tcx), }, ); - true } - }; + } } ongoing_codegen.codegen_finished(tcx); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index d6c23012762..92792ab6477 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_ast::{ast, MetaItemKind, NestedMetaItem}; +use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_errors::struct_span_err; use rustc_hir as hir; @@ -60,6 +60,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } + // When `no_builtins` is applied at the crate level, we should add the + // `no-builtins` attribute to each function to ensure it takes effect in LTO. + let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); + let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); + if no_builtins { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; + } + let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); let mut inline_span = None; @@ -493,7 +501,22 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { }); // #73631: closures inherit `#[target_feature]` annotations - if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) { + // + // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`. + // + // At this point, `unsafe` has already been checked and `#[target_feature]` only affects codegen. + // Emitting both `#[inline(always)]` and `#[target_feature]` can potentially result in an + // ICE, because LLVM errors when the function fails to be inlined due to a target feature + // mismatch. + // + // Using `#[inline(always)]` implies that this closure will most likely be inlined into + // its parent function, which effectively inherits the features anyway. Boxing this closure + // would result in this closure being compiled without the inherited target features, but this + // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute. + if tcx.features().target_feature_11 + && tcx.is_closure(did.to_def_id()) + && codegen_fn_attrs.inline != InlineAttr::Always + { let owner_id = tcx.parent(did.to_def_id()); if tcx.def_kind(owner_id).has_codegen_attrs() { codegen_fn_attrs diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 64f799bb1e6..067c824aba0 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -414,7 +414,7 @@ fn push_debuginfo_type_name<'tcx>( } // Type parameters from polymorphized functions. ty::Param(_) => { - write!(output, "{:?}", t).unwrap(); + write!(output, "{t:?}").unwrap(); } ty::Error(_) | ty::Infer(_) @@ -565,9 +565,9 @@ fn push_disambiguated_special_name( output: &mut String, ) { if cpp_like_debuginfo { - write!(output, "{}${}", label, disambiguator).unwrap(); + write!(output, "{label}${disambiguator}").unwrap(); } else { - write!(output, "{{{}#{}}}", label, disambiguator).unwrap(); + write!(output, "{{{label}#{disambiguator}}}").unwrap(); } } @@ -651,15 +651,15 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S ty::Int(ity) => { let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; - write!(output, "{}", val) + write!(output, "{val}") } ty::Uint(_) => { let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); - write!(output, "{}", val) + write!(output, "{val}") } ty::Bool => { let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); - write!(output, "{}", val) + write!(output, "{val}") } _ => { // If we cannot evaluate the constant to a known type, we fall back @@ -678,9 +678,9 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S }); if cpp_like_debuginfo(tcx) { - write!(output, "CONST${:x}", hash_short) + write!(output, "CONST${hash_short:x}") } else { - write!(output, "{{CONST#{:x}}}", hash_short) + write!(output, "{{CONST#{hash_short:x}}}") } } }, @@ -752,7 +752,7 @@ fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { } fn pop_close_angle_bracket(output: &mut String) { - assert!(output.ends_with('>'), "'output' does not end with '>': {}", output); + assert!(output.ends_with('>'), "'output' does not end with '>': {output}"); output.pop(); if output.ends_with(' ') { output.pop(); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 056b4abd235..b7d8b9b45bf 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -177,31 +177,31 @@ impl IntoDiagnostic<'_> for ThorinErrorWrapper { } thorin::Error::NamelessSection(_, offset) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_section_without_name); - diag.set_arg("offset", format!("0x{:08x}", offset)); + diag.set_arg("offset", format!("0x{offset:08x}")); diag } thorin::Error::RelocationWithInvalidSymbol(section, offset) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol); diag.set_arg("section", section); - diag.set_arg("offset", format!("0x{:08x}", offset)); + diag.set_arg("offset", format!("0x{offset:08x}")); diag } thorin::Error::MultipleRelocations(section, offset) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_multiple_relocations); diag.set_arg("section", section); - diag.set_arg("offset", format!("0x{:08x}", offset)); + diag.set_arg("offset", format!("0x{offset:08x}")); diag } thorin::Error::UnsupportedRelocation(section, offset) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_unsupported_relocation); diag.set_arg("section", section); - diag.set_arg("offset", format!("0x{:08x}", offset)); + diag.set_arg("offset", format!("0x{offset:08x}")); diag } thorin::Error::MissingDwoName(id) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_missing_dwo_name); - diag.set_arg("id", format!("0x{:08x}", id)); + diag.set_arg("id", format!("0x{id:08x}")); diag } thorin::Error::NoCompilationUnits => { @@ -251,7 +251,7 @@ impl IntoDiagnostic<'_> for ThorinErrorWrapper { } thorin::Error::StrAtOffset(_, offset) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_str_at_offset); - diag.set_arg("offset", format!("0x{:08x}", offset)); + diag.set_arg("offset", format!("0x{offset:08x}")); diag } thorin::Error::ParseIndex(_, section) => { @@ -261,7 +261,7 @@ impl IntoDiagnostic<'_> for ThorinErrorWrapper { } thorin::Error::UnitNotInIndex(unit) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_unit_not_in_index); - diag.set_arg("unit", format!("0x{:08x}", unit)); + diag.set_arg("unit", format!("0x{unit:08x}")); diag } thorin::Error::RowNotInIndex(_, row) => { @@ -275,7 +275,7 @@ impl IntoDiagnostic<'_> for ThorinErrorWrapper { } thorin::Error::EmptyUnit(unit) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_empty_unit); - diag.set_arg("unit", format!("0x{:08x}", unit)); + diag.set_arg("unit", format!("0x{unit:08x}")); diag } thorin::Error::MultipleDebugInfoSection => { @@ -292,12 +292,12 @@ impl IntoDiagnostic<'_> for ThorinErrorWrapper { } thorin::Error::DuplicateUnit(unit) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_duplicate_unit); - diag.set_arg("unit", format!("0x{:08x}", unit)); + diag.set_arg("unit", format!("0x{unit:08x}")); diag } thorin::Error::MissingReferencedUnit(unit) => { diag = handler.struct_err(fluent::codegen_ssa_thorin_missing_referenced_unit); - diag.set_arg("unit", format!("0x{:08x}", unit)); + diag.set_arg("unit", format!("0x{unit:08x}")); diag } thorin::Error::NoOutputObjectCreated => { @@ -353,7 +353,7 @@ impl IntoDiagnostic<'_> for LinkingFailed<'_> { let contains_undefined_ref = self.escaped_output.contains("undefined reference to"); - diag.note(format!("{:?}", self.command)).note(self.escaped_output.to_string()); + diag.note(format!("{:?}", self.command)).note(self.escaped_output); // Trying to match an error from OS linkers // which by now we have no way to translate. @@ -456,6 +456,12 @@ pub struct LinkerFileStem; pub struct StaticLibraryNativeArtifacts; #[derive(Diagnostic)] +#[diag(codegen_ssa_static_library_native_artifacts_to_file)] +pub struct StaticLibraryNativeArtifactsToFile<'a> { + pub path: &'a Path, +} + +#[derive(Diagnostic)] #[diag(codegen_ssa_link_script_unavailable)] pub struct LinkScriptUnavailable; diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 9abe7b25d0e..12146a54d3b 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -23,7 +23,6 @@ impl<'a, 'tcx> VirtualIndex { // Load the data pointer from the object. debug!("get_fn({llvtable:?}, {ty:?}, {self:?})"); let llty = bx.fn_ptr_backend_type(fn_abi); - let llvtable = bx.pointercast(llvtable, bx.type_ptr_to(llty)); if bx.cx().sess().opts.unstable_opts.virtual_function_elimination && bx.cx().sess().lto() == Lto::Fat @@ -33,7 +32,7 @@ impl<'a, 'tcx> VirtualIndex { .unwrap(); let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes(); let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); - bx.pointercast(func, llty) + func } else { let ptr_align = bx.tcx().data_layout.pointer_align.abi; let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]); @@ -54,7 +53,6 @@ impl<'a, 'tcx> VirtualIndex { debug!("get_int({:?}, {:?})", llvtable, self); let llty = bx.type_isize(); - let llvtable = bx.pointercast(llvtable, bx.type_ptr_to(llty)); let usize_align = bx.tcx().data_layout.pointer_align.abi; let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]); let ptr = bx.load(llty, gep, usize_align); diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index ed608bdbe9a..f13dfd96b8e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -439,8 +439,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"), }; let ty = bx.cast_backend_type(cast_ty); - let addr = bx.pointercast(llslot, bx.type_ptr_to(ty)); - bx.load(ty, addr, self.fn_abi.ret.layout.align.abi) + bx.load(ty, llslot, self.fn_abi.ret.layout.align.abi) } }; bx.ret(llval); @@ -703,13 +702,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { with_no_trimmed_paths!({ if layout.abi.is_uninhabited() { // Use this error even for the other intrinsics as it is more precise. - format!("attempted to instantiate uninhabited type `{}`", ty) + format!("attempted to instantiate uninhabited type `{ty}`") } else if requirement == ValidityRequirement::Zero { - format!("attempted to zero-initialize type `{}`, which is invalid", ty) + format!("attempted to zero-initialize type `{ty}`, which is invalid") } else { format!( - "attempted to leave type `{}` uninitialized, which is invalid", - ty + "attempted to leave type `{ty}` uninitialized, which is invalid" ) } }) @@ -853,9 +851,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some(intrinsic) => { let dest = match ret_dest { _ if fn_abi.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => { - bx.const_undef(bx.type_ptr_to(bx.arg_memory_ty(&fn_abi.ret))) - } + ReturnDest::Nothing => bx.const_undef(bx.type_ptr()), ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.llval, ReturnDest::DirectOperand(_) => { bug!("Cannot use direct operand with an intrinsic call") @@ -1045,10 +1041,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { assert_eq!( fn_abi.args.len(), mir_args + 1, - "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR: {:?} {:?} {:?}", - instance, - fn_span, - fn_abi, + "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR: {instance:?} {fn_span:?} {fn_abi:?}", ); let location = self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); @@ -1428,8 +1421,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Have to load the argument, maybe while casting it. if let PassMode::Cast(ty, _) = &arg.mode { let llty = bx.cast_backend_type(ty); - let addr = bx.pointercast(llval, bx.type_ptr_to(llty)); - llval = bx.load(llty, addr, align.min(arg.layout.align.abi)); + llval = bx.load(llty, llval, align.min(arg.layout.align.abi)); } else { // We can't use `PlaceRef::load` here because the argument // may have a type we don't treat as immediate, but the ABI @@ -1555,7 +1547,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock { let llbb = self.llbb(bb); if base::wants_new_eh_instructions(self.cx.sess()) { - let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb)); + let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{bb:?}")); let mut cleanup_bx = Bx::build(self.cx, cleanup_bb); let funclet = cleanup_bx.cleanup_pad(None, &[]); cleanup_bx.br(llbb); @@ -1634,7 +1626,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // represents that this is a catch-all block. bx = Bx::build(self.cx, cp_llbb); let null = - bx.const_null(bx.type_i8p_ext(bx.cx().data_layout().instruction_address_space)); + bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space)); let sixty_four = bx.const_i32(64); funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null])); } else { @@ -1675,7 +1667,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match self.cached_llbbs[bb] { CachedLlbb::None => { // FIXME(eddyb) only name the block if `fewer_names` is `false`. - let llbb = Bx::append_block(self.cx, self.llfn, &format!("{:?}", bb)); + let llbb = Bx::append_block(self.cx, self.llfn, &format!("{bb:?}")); self.cached_llbbs[bb] = CachedLlbb::Some(llbb); Some(llbb) } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 1c5031dfc4b..babcf9bee24 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -65,8 +65,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, constant: &mir::Constant<'tcx>, ) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> { - let uv = match constant.literal { + let uv = match self.monomorphize(constant.literal) { mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(), + mir::ConstantKind::Ty(c) => match c.kind() { + // A constant that came from a const generic but was then used as an argument to old-style + // simd_shuffle (passing as argument instead of as a generic param). + rustc_type_ir::ConstKind::Value(valtree) => return Ok(Some(valtree)), + other => span_bug!(constant.span, "{other:#?}"), + }, + // We should never encounter `ConstantKind::Val` unless MIR opts (like const prop) evaluate + // a constant and write that value back into `Operand`s. This could happen, but is unlikely. + // Also: all users of `simd_shuffle` are on unstable and already need to take a lot of care + // around intrinsics. For an issue to happen here, it would require a macro expanding to a + // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but + // the user pass through arbitrary expressions. + // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real + // const generic. other => span_bug!(constant.span, "{other:#?}"), }; let uv = self.monomorphize(uv); diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 34b8d8b5a6f..4167a85ccd5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -337,7 +337,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { Some(match whole_local_var.or(fallback_var.clone()) { Some(var) if var.name != kw::Empty => var.name.to_string(), - _ => format!("{:?}", local), + _ => format!("{local:?}"), }) }; diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 18201abd631..8821fb21fd0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -270,7 +270,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::const_allocate => { // returns a null pointer at runtime. - bx.const_null(bx.type_i8p()) + bx.const_null(bx.type_ptr()) } sym::const_deallocate => { @@ -310,14 +310,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = fn_args.type_at(0); if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { let weak = instruction == "cxchgweak"; - let mut dst = args[0].immediate(); + let dst = args[0].immediate(); let mut cmp = args[1].immediate(); let mut src = args[2].immediate(); if ty.is_unsafe_ptr() { // Some platforms do not support atomic operations on pointers, // so we cast to integer first. - let ptr_llty = bx.type_ptr_to(bx.type_isize()); - dst = bx.pointercast(dst, ptr_llty); cmp = bx.ptrtoint(cmp, bx.type_isize()); src = bx.ptrtoint(src, bx.type_isize()); } @@ -342,13 +340,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { let layout = bx.layout_of(ty); let size = layout.size; - let mut source = args[0].immediate(); + let source = args[0].immediate(); if ty.is_unsafe_ptr() { // Some platforms do not support atomic operations on pointers, // so we cast to integer first... let llty = bx.type_isize(); - let ptr_llty = bx.type_ptr_to(llty); - source = bx.pointercast(source, ptr_llty); let result = bx.atomic_load(llty, source, parse_ordering(bx, ordering), size); // ... and then cast the result back to a pointer bx.inttoptr(result, bx.backend_type(layout)) @@ -365,12 +361,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { let size = bx.layout_of(ty).size; let mut val = args[1].immediate(); - let mut ptr = args[0].immediate(); + let ptr = args[0].immediate(); if ty.is_unsafe_ptr() { // Some platforms do not support atomic operations on pointers, // so we cast to integer first. - let ptr_llty = bx.type_ptr_to(bx.type_isize()); - ptr = bx.pointercast(ptr, ptr_llty); val = bx.ptrtoint(val, bx.type_isize()); } bx.atomic_store(val, ptr, parse_ordering(bx, ordering), size); @@ -409,13 +403,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = fn_args.type_at(0); if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { - let mut ptr = args[0].immediate(); + let ptr = args[0].immediate(); let mut val = args[1].immediate(); if ty.is_unsafe_ptr() { // Some platforms do not support atomic operations on pointers, // so we cast to integer first. - let ptr_llty = bx.type_ptr_to(bx.type_isize()); - ptr = bx.pointercast(ptr, ptr_llty); val = bx.ptrtoint(val, bx.type_isize()); } bx.atomic_rmw(atom_op, ptr, val, parse_ordering(bx, ordering)) @@ -470,10 +462,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; if !fn_abi.ret.is_ignore() { - if let PassMode::Cast(ty, _) = &fn_abi.ret.mode { - let ptr_llty = bx.type_ptr_to(bx.cast_backend_type(ty)); - let ptr = bx.pointercast(result.llval, ptr_llty); - bx.store(llval, ptr, result.align); + if let PassMode::Cast(..) = &fn_abi.ret.mode { + bx.store(llval, result.llval, result.align); } else { OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) .val diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 31c293d7c29..f90d1a0fc9c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -2,7 +2,6 @@ use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; use crate::base; -use crate::common::TypeKind; use crate::glue; use crate::traits::*; use crate::MemFlags; @@ -132,7 +131,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { ) -> Self { let alloc_align = alloc.inner().align; assert_eq!(alloc_align, layout.align.abi); - let ty = bx.type_ptr_to(bx.cx().backend_type(layout)); let read_scalar = |start, size, s: abi::Scalar, ty| { let val = alloc @@ -156,7 +154,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => { let size = s.size(bx); assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - let val = read_scalar(Size::ZERO, size, s, ty); + let val = read_scalar(Size::ZERO, size, s, bx.type_ptr()); OperandRef { val: OperandValue::Immediate(val), layout } } Abi::ScalarPair( @@ -187,7 +185,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let base_addr = bx.static_addr_of(init, alloc_align, None); let llval = bx.const_ptr_byte_offset(base_addr, offset); - let llval = bx.const_bitcast(llval, ty); bx.load_operand(PlaceRef::new_sized(llval, layout)) } } @@ -314,38 +311,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { ) => { // Bools in union fields needs to be truncated. *llval = bx.to_immediate(*llval, field); - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - let ty = bx.cx().immediate_backend_type(field); - if bx.type_kind(ty) == TypeKind::Pointer { - *llval = bx.pointercast(*llval, ty); - } } (OperandValue::Pair(a, b), Abi::ScalarPair(a_abi, b_abi)) => { // Bools in union fields needs to be truncated. *a = bx.to_immediate_scalar(*a, a_abi); *b = bx.to_immediate_scalar(*b, b_abi); - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - let a_ty = bx.cx().scalar_pair_element_backend_type(field, 0, true); - let b_ty = bx.cx().scalar_pair_element_backend_type(field, 1, true); - if bx.type_kind(a_ty) == TypeKind::Pointer { - *a = bx.pointercast(*a, a_ty); - } - if bx.type_kind(b_ty) == TypeKind::Pointer { - *b = bx.pointercast(*b, b_ty); - } } // Newtype vector of array, e.g. #[repr(simd)] struct S([i32; 4]); (OperandValue::Immediate(llval), Abi::Aggregate { sized: true }) => { assert!(matches!(self.layout.abi, Abi::Vector { .. })); - let llty = bx.cx().backend_type(self.layout); let llfield_ty = bx.cx().backend_type(field); // Can't bitcast an aggregate, so round trip through memory. - let lltemp = bx.alloca(llfield_ty, field.align.abi); - let llptr = bx.pointercast(lltemp, bx.cx().type_ptr_to(llty)); + let llptr = bx.alloca(llfield_ty, field.align.abi); bx.store(*llval, llptr, field.align.abi); - *llval = bx.load(llfield_ty, lltemp, field.align.abi); + *llval = bx.load(llfield_ty, llptr, field.align.abi); } (OperandValue::Immediate(_), Abi::Uninhabited | Abi::Aggregate { sized: false }) => { bug!() @@ -380,9 +361,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { let ibty1 = bx.cx().scalar_pair_element_backend_type(layout, 1, true); OperandValue::Pair(bx.const_poison(ibty0), bx.const_poison(ibty1)) } else { - let bty = bx.cx().backend_type(layout); - let ptr_bty = bx.cx().type_ptr_to(bty); - OperandValue::Ref(bx.const_poison(ptr_bty), None, layout.align.abi) + let ptr = bx.cx().type_ptr(); + OperandValue::Ref(bx.const_poison(ptr), None, layout.align.abi) } } @@ -434,8 +414,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { if flags.contains(MemFlags::NONTEMPORAL) { // HACK(nox): This is inefficient but there is no nontemporal memcpy. let ty = bx.backend_type(dest.layout); - let ptr = bx.pointercast(r, bx.type_ptr_to(ty)); - let val = bx.load(ty, ptr, source_align); + let val = bx.load(ty, r, source_align); bx.store_with_flags(val, dest.llval, dest.align, flags); return; } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 90eab55f76e..64c6d17469b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -115,8 +115,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } if field.is_zst() => { // ZST fields are not included in Scalar, ScalarPair, and Vector layouts, so manually offset the pointer. - let byte_ptr = bx.pointercast(self.llval, bx.cx().type_i8p()); - bx.gep(bx.cx().type_i8(), byte_ptr, &[bx.const_usize(offset.bytes())]) + bx.gep(bx.cx().type_i8(), self.llval, &[bx.const_usize(offset.bytes())]) } Abi::Scalar(_) | Abi::ScalarPair(..) => { // All fields of Scalar and ScalarPair layouts must have been handled by this point. @@ -133,8 +132,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } }; PlaceRef { - // HACK(eddyb): have to bitcast pointers until LLVM removes pointee types. - llval: bx.pointercast(llval, bx.cx().type_ptr_to(bx.cx().backend_type(field))), + llval, llextra: if bx.cx().type_has_metadata(field.ty) { self.llextra } else { None }, layout: field, align: effective_field_align, @@ -194,20 +192,10 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { debug!("struct_field_ptr: DST field offset: {:?}", offset); - // Cast and adjust pointer. - let byte_ptr = bx.pointercast(self.llval, bx.cx().type_i8p()); - let byte_ptr = bx.gep(bx.cx().type_i8(), byte_ptr, &[offset]); + // Adjust pointer. + let ptr = bx.gep(bx.cx().type_i8(), self.llval, &[offset]); - // Finally, cast back to the type expected. - let ll_fty = bx.cx().backend_type(field); - debug!("struct_field_ptr: Field type is {:?}", ll_fty); - - PlaceRef { - llval: bx.pointercast(byte_ptr, bx.cx().type_ptr_to(ll_fty)), - llextra: self.llextra, - layout: field, - align: effective_field_align, - } + PlaceRef { llval: ptr, llextra: self.llextra, layout: field, align: effective_field_align } } /// Obtain the actual discriminant of a value. @@ -416,11 +404,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ) -> Self { let mut downcast = *self; downcast.layout = self.layout.for_variant(bx.cx(), variant_index); - - // Cast to the appropriate variant struct type. - let variant_ty = bx.cx().backend_type(downcast.layout); - downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty)); - downcast } @@ -431,11 +414,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { ) -> Self { let mut downcast = *self; downcast.layout = bx.cx().layout_of(ty); - - // Cast to the appropriate type. - let variant_ty = bx.cx().backend_type(downcast.layout); - downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty)); - downcast } @@ -515,13 +493,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { )); } - // Cast the place pointer type to the new - // array or slice type (`*[%_; new_len]`). - subslice.llval = bx.pointercast( - subslice.llval, - bx.cx().type_ptr_to(bx.cx().backend_type(subslice.layout)), - ); - subslice } mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 1f90a28eb8e..07c61df2140 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -182,9 +182,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate(..) | OperandValue::Pair(..) => { // When we have immediate(s), the alignment of the source is irrelevant, // so we can store them using the destination's alignment. - let llty = bx.backend_type(src.layout); - let cast_ptr = bx.pointercast(dst.llval, bx.type_ptr_to(llty)); - src.val.store(bx, PlaceRef::new_sized_aligned(cast_ptr, src.layout, dst.align)); + src.val.store(bx, PlaceRef::new_sized_aligned(dst.llval, src.layout, dst.align)); } } } @@ -222,9 +220,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Ref(ptr, meta, align) => { debug_assert_eq!(meta, None); debug_assert!(matches!(operand_kind, OperandValueKind::Ref)); - let cast_bty = bx.backend_type(cast); - let cast_ptr = bx.pointercast(ptr, bx.type_ptr_to(cast_bty)); - let fake_place = PlaceRef::new_sized_aligned(cast_ptr, cast, align); + let fake_place = PlaceRef::new_sized_aligned(ptr, cast, align); Some(bx.load_operand(fake_place).val) } OperandValue::ZeroSized => { @@ -397,8 +393,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) -> OperandRef<'tcx, Bx::Value> { assert!( self.rvalue_creates_operand(rvalue, DUMMY_SP), - "cannot codegen {:?} to operand", - rvalue, + "cannot codegen {rvalue:?} to operand", ); match *rvalue { @@ -480,18 +475,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { { if let OperandValue::Pair(data_ptr, meta) = operand.val { if bx.cx().is_backend_scalar_pair(cast) { - let data_cast = bx.pointercast( - data_ptr, - bx.cx().scalar_pair_element_backend_type(cast, 0, true), - ); - OperandValue::Pair(data_cast, meta) + OperandValue::Pair(data_ptr, meta) } else { - // cast to thin-ptr - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and - // pointer-cast of that pointer to desired pointer type. - let llcast_ty = bx.cx().immediate_backend_type(cast); - let llval = bx.pointercast(data_ptr, llcast_ty); - OperandValue::Immediate(llval) + // Cast of fat-ptr to thin-ptr is an extraction of data-ptr. + OperandValue::Immediate(data_ptr) } } else { bug!("unexpected non-pair operand"); @@ -736,13 +723,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::ShallowInitBox(ref operand, content_ty) => { let operand = self.codegen_operand(bx, operand); - let lloperand = operand.immediate(); + let val = operand.immediate(); let content_ty = self.monomorphize(content_ty); let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty)); - let llty_ptr = bx.cx().backend_type(box_layout); - let val = bx.pointercast(lloperand, llty_ptr); OperandRef { val: OperandValue::Immediate(val), layout: box_layout } } } diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index b72a1a07866..6fbf992eda9 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -140,8 +140,8 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { MonoItem::Fn(instance) => { format!("Fn({:?}, {})", instance.def, instance.args.as_ptr().addr()) } - MonoItem::Static(id) => format!("Static({:?})", id), - MonoItem::GlobalAsm(id) => format!("GlobalAsm({:?})", id), + MonoItem::Static(id) => format!("Static({id:?})"), + MonoItem::GlobalAsm(id) => format!("GlobalAsm({id:?})"), } } } diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 7b5d83c612a..98e561b0aef 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -321,7 +321,7 @@ pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Opt "aarch64" => AARCH64_ALLOWED_FEATURES, "x86" | "x86_64" => X86_ALLOWED_FEATURES, "hexagon" => HEXAGON_ALLOWED_FEATURES, - "mips" | "mips64" => MIPS_ALLOWED_FEATURES, + "mips" | "mips32r6" | "mips64" | "mips64r6" => MIPS_ALLOWED_FEATURES, "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES, @@ -369,9 +369,9 @@ pub fn from_target_feature( // We allow comma separation to enable multiple features. target_features.extend(value.as_str().split(',').filter_map(|feature| { let Some(feature_gate) = supported_target_features.get(feature) else { - let msg = format!("the feature named `{}` is not valid for this target", feature); + let msg = format!("the feature named `{feature}` is not valid for this target"); let mut err = tcx.sess.struct_span_err(item.span(), msg); - err.span_label(item.span(), format!("`{}` is not valid for this target", feature)); + err.span_label(item.span(), format!("`{feature}` is not valid for this target")); if let Some(stripped) = feature.strip_prefix('+') { let valid = supported_target_features.contains_key(stripped); if valid { @@ -405,7 +405,7 @@ pub fn from_target_feature( &tcx.sess.parse_sess, feature_gate.unwrap(), item.span(), - format!("the target feature `{}` is currently unstable", feature), + format!("the target feature `{feature}` is currently unstable"), ) .emit(); } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index b3c9ecf8b93..0a02ca6b317 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -23,6 +23,8 @@ use rustc_span::symbol::Symbol; use rustc_target::abi::call::FnAbi; use rustc_target::spec::Target; +use std::fmt; + pub trait BackendTypes { type Value: CodegenObject; type Function: CodegenObject; @@ -61,7 +63,7 @@ pub trait CodegenBackend { fn locale_resource(&self) -> &'static str; fn init(&self, _sess: &Session) {} - fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn print(&self, _req: &PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {} fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { vec![] } @@ -140,15 +142,6 @@ pub trait ExtraBackendMethods: target_features: &[String], ) -> TargetMachineFactoryFn<Self>; - fn spawn_thread<F, T>(_time_trace: bool, f: F) -> std::thread::JoinHandle<T> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - std::thread::spawn(f) - } - fn spawn_named_thread<F, T>( _time_trace: bool, name: String, @@ -162,3 +155,19 @@ pub trait ExtraBackendMethods: std::thread::Builder::new().name(name).spawn(f) } } + +pub trait PrintBackendInfo { + fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>); +} + +impl PrintBackendInfo for String { + fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) { + fmt::Write::write_fmt(self, args).unwrap(); + } +} + +impl dyn PrintBackendInfo + '_ { + pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) { + self.infallible_write_fmt(args); + } +} diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index d6e9bfce1a4..4dff9c7684f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -5,7 +5,13 @@ use rustc_target::abi; pub trait ConstMethods<'tcx>: BackendTypes { // Constant constructors fn const_null(&self, t: Self::Type) -> Self::Value; + /// Generate an uninitialized value (matching uninitialized memory in MIR). + /// Whether memory is initialized or not is tracked byte-for-byte. fn const_undef(&self, t: Self::Type) -> Self::Value; + /// Generate a fake value. Poison always affects the entire value, even if just a single byte is + /// poison. This can only be used in codepaths that are already UB, i.e., UB-free Rust code + /// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a + /// poison value. fn const_poison(&self, t: Self::Type) -> Self::Value; fn const_int(&self, t: Self::Type, i: i64) -> Self::Value; fn const_uint(&self, t: Self::Type, i: u64) -> Self::Value; @@ -30,7 +36,6 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; - fn const_ptrcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value; fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 8cb58bd4c70..728c2bc8c49 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -30,7 +30,9 @@ mod write; pub use self::abi::AbiBuilderMethods; pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; +pub use self::backend::{ + Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo, +}; pub use self::builder::{BuilderMethods, OverflowOp}; pub use self::consts::ConstMethods; pub use self::coverageinfo::CoverageInfoBuilderMethods; diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index e64417e1a4a..dc3dbd9d819 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -26,8 +26,8 @@ pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type; fn type_struct(&self, els: &[Self::Type], packed: bool) -> Self::Type; fn type_kind(&self, ty: Self::Type) -> TypeKind; - fn type_ptr_to(&self, ty: Self::Type) -> Self::Type; - fn type_ptr_to_ext(&self, ty: Self::Type, address_space: AddressSpace) -> Self::Type; + fn type_ptr(&self) -> Self::Type; + fn type_ptr_ext(&self, address_space: AddressSpace) -> Self::Type; fn element_type(&self, ty: Self::Type) -> Self::Type; /// Returns the number of elements in `self` if it is a LLVM vector type. @@ -42,14 +42,6 @@ pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { } pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { - fn type_i8p(&self) -> Self::Type { - self.type_i8p_ext(AddressSpace::DATA) - } - - fn type_i8p_ext(&self, address_space: AddressSpace) -> Self::Type { - self.type_ptr_to_ext(self.type_i8(), address_space) - } - fn type_int(&self) -> Self::Type { match &self.sess().target.c_int_width[..] { "16" => self.type_i16(), diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 9826256a4c5..ecf5095d8a3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,5 +1,5 @@ use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; -use crate::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; +use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; use crate::{CompiledModule, ModuleCodegen}; use rustc_errors::{FatalError, Handler}; @@ -23,7 +23,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { /// for further optimization. fn run_fat_lto( cgcx: &CodegenContext<Self>, - modules: Vec<FatLTOInput<Self>>, + modules: Vec<FatLtoInput<Self>>, cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, ) -> Result<LtoModuleCodegen<Self>, FatalError>; /// Performs thin LTO by performing necessary global analysis and returning two @@ -35,6 +35,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>, ) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError>; fn print_pass_timings(&self); + fn print_statistics(&self); unsafe fn optimize( cgcx: &CodegenContext<Self>, diag_handler: &Handler, diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 74030a43c50..4e47fed8640 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/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d8eade5bd2a..671c2be1de9 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -408,8 +408,11 @@ const_eval_undefined_behavior = const_eval_undefined_behavior_note = The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. +const_eval_uninhabited_enum_tag = {$front_matter}: encountered an uninhabited enum variant +const_eval_uninhabited_enum_variant_read = + read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = - writing discriminant of an uninhabited enum + writing discriminant of an uninhabited enum variant const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}` const_eval_uninit = {$front_matter}: encountered uninitialized bytes const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean @@ -423,8 +426,6 @@ const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but e const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str` -const_eval_uninit_unsized_local = - unsized local is used while uninitialized const_eval_unreachable = entering unreachable code const_eval_unreachable_unwind = unwinding past a stack frame that does not allow unwinding diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index ffeff8d079a..d39a7e8a192 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -138,7 +138,10 @@ where err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { ErrorHandled::TooGeneric } - err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(error_reported), + err_inval!(AlreadyReported(guar)) => ErrorHandled::Reported(guar), + err_inval!(Layout(LayoutError::ReferencesError(guar))) => { + ErrorHandled::Reported(guar.into()) + } err_inval!(Layout(layout_error @ LayoutError::SizeOverflow(_))) => { // We must *always* hard error on these, even if the caller wants just a lint. // The `message` makes little sense here, this is a more serious error than the 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 b21cb984de6..4c7e9194401 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -52,13 +52,13 @@ fn eval_body_using_ecx<'mir, 'tcx>( trace!( "eval_body_using_ecx: pushing stack frame for global: {}{}", with_no_trimmed_paths!(ecx.tcx.def_path_str(cid.instance.def_id())), - cid.promoted.map_or_else(String::new, |p| format!("::promoted[{:?}]", p)) + cid.promoted.map_or_else(String::new, |p| format!("::promoted[{p:?}]")) ); ecx.push_stack_frame( cid.instance, body, - &ret.into(), + &ret.clone().into(), StackPopCleanup::Root { cleanup: false }, )?; @@ -228,7 +228,6 @@ pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToConstValueResult<'tcx> { - assert!(key.param_env.is_const()); // see comment in eval_to_allocation_raw_provider for what we're doing here if key.param_env.reveal() == Reveal::All { let mut key = key; @@ -269,7 +268,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { - assert!(key.param_env.is_const()); // Because the constant is computed twice (once per value of `Reveal`), we are at risk of // reporting the same error twice here. To resolve this, we check whether we can evaluate the // constant in the more restrictive `Reveal::UserFacing`, which most likely already was @@ -356,7 +354,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/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index fa8253d5e49..cc39387c41f 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -28,15 +28,18 @@ pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { && tcx.constness(parent_id) == hir::Constness::Const } -/// Checks whether an item is considered to be `const`. If it is a constructor, it is const. If -/// it is a trait impl/function, return if it has a `const` modifier. If it is an intrinsic, -/// report whether said intrinsic has a `rustc_const_{un,}stable` attribute. Otherwise, return -/// `Constness::NotConst`. +/// Checks whether an item is considered to be `const`. If it is a constructor, anonymous const, +/// const block, const item or associated const, it is const. If it is a trait impl/function, +/// return if it has a `const` modifier. If it is an intrinsic, report whether said intrinsic +/// has a `rustc_const_{un,}stable` attribute. Otherwise, return `Constness::NotConst`. fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness { let node = tcx.hir().get_by_def_id(def_id); match node { - hir::Node::Ctor(_) => hir::Constness::Const, + hir::Node::Ctor(_) + | hir::Node::AnonConst(_) + | hir::Node::ConstBlock(_) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => hir::Constness::Const, hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness, hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => { // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 267795a6cb4..6630eeca27e 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/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 0a3c3914ff9..ef31155215a 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -101,8 +101,8 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>( return None; } ty::Adt(def, _) => { - let variant = ecx.read_discriminant(&op).ok()?.1; - let down = ecx.operand_downcast(&op, variant).ok()?; + let variant = ecx.read_discriminant(&op).ok()?; + let down = ecx.project_downcast(&op, variant).ok()?; (def.variants()[variant].fields.len(), Some(variant), down) } ty::Tuple(args) => (args.len(), None, op), @@ -111,7 +111,7 @@ pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>( let fields_iter = (0..field_count) .map(|i| { - let field_op = ecx.operand_field(&down, i).ok()?; + let field_op = ecx.project_field(&down, i).ok()?; let val = op_to_const(&ecx, &field_op); Some((val, field_op.layout.ty)) }) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 9531d973eb3..7c1dbddfc26 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -2,11 +2,11 @@ use super::eval_queries::{mk_eval_cx, op_to_const}; use super::machine::CompileTimeEvalContext; use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES}; use crate::const_eval::CanAccessStatics; +use crate::interpret::MPlaceTy; use crate::interpret::{ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta, - MemoryKind, PlaceTy, Scalar, + MemoryKind, Place, Projectable, Scalar, }; -use crate::interpret::{MPlaceTy, Value}; use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt}; use rustc_span::source_map::DUMMY_SP; use rustc_target::abi::{Align, FieldIdx, VariantIdx, FIRST_VARIANT}; @@ -20,15 +20,15 @@ fn branches<'tcx>( num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { let place = match variant { - Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(), - None => *place, + Some(variant) => ecx.project_downcast(place, variant).unwrap(), + None => place.clone(), }; let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32())))); debug!(?place, ?variant); let mut fields = Vec::with_capacity(n); for i in 0..n { - let field = ecx.mplace_field(&place, i).unwrap(); + let field = ecx.project_field(&place, i).unwrap(); let valtree = const_to_valtree_inner(ecx, &field, num_nodes)?; fields.push(Some(valtree)); } @@ -55,13 +55,11 @@ fn slice_branches<'tcx>( place: &MPlaceTy<'tcx>, num_nodes: &mut usize, ) -> ValTreeCreationResult<'tcx> { - let n = place - .len(&ecx.tcx.tcx) - .unwrap_or_else(|_| panic!("expected to use len of place {:?}", place)); + let n = place.len(ecx).unwrap_or_else(|_| panic!("expected to use len of place {place:?}")); let mut elems = Vec::with_capacity(n as usize); for i in 0..n { - let place_elem = ecx.mplace_index(place, i).unwrap(); + let place_elem = ecx.project_index(place, i).unwrap(); let valtree = const_to_valtree_inner(ecx, &place_elem, num_nodes)?; elems.push(valtree); } @@ -88,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(); @@ -104,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); @@ -132,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) @@ -282,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) @@ -291,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() { @@ -331,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 @@ -347,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() { @@ -371,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(); @@ -386,12 +384,12 @@ fn valtree_into_mplace<'tcx>( debug!(?variant); ( - place.project_downcast(ecx, variant_idx).unwrap(), + ecx.project_downcast(place, variant_idx).unwrap(), &branches[1..], Some(variant_idx), ) } - _ => (*place, branches, None), + _ => (place.clone(), branches, None), }; debug!(?place_adjusted, ?branches); @@ -400,8 +398,8 @@ fn valtree_into_mplace<'tcx>( for (i, inner_valtree) in branches.iter().enumerate() { debug!(?i, ?inner_valtree); - let mut place_inner = match ty.kind() { - ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(), + 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 => { @@ -441,29 +439,29 @@ fn valtree_into_mplace<'tcx>( ) .unwrap() } - _ => ecx.mplace_field(&place_adjusted, i).unwrap(), + _ => ecx.project_field(&place_adjusted, i).unwrap(), }; 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/errors.rs b/compiler/rustc_const_eval/src/errors.rs index ca38cce710e..e1109e584b7 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -511,7 +511,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes, DeadLocal => const_eval_dead_local, ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch, - UninhabitedEnumVariantWritten => const_eval_uninhabited_enum_variant_written, + UninhabitedEnumVariantWritten(_) => const_eval_uninhabited_enum_variant_written, + UninhabitedEnumVariantRead(_) => const_eval_uninhabited_enum_variant_read, Validation(e) => e.diagnostic_message(), Custom(x) => (x.msg)(), } @@ -535,7 +536,8 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { | InvalidMeta(InvalidMetaKind::TooBig) | InvalidUninitBytes(None) | DeadLocal - | UninhabitedEnumVariantWritten => {} + | UninhabitedEnumVariantWritten(_) + | UninhabitedEnumVariantRead(_) => {} BoundsCheckFailed { len, index } => { builder.set_arg("len", len); builder.set_arg("index", index); @@ -623,6 +625,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { UnsafeCell => const_eval_unsafe_cell, UninhabitedVal { .. } => const_eval_uninhabited_val, InvalidEnumTag { .. } => const_eval_invalid_enum_tag, + UninhabitedEnumTag => const_eval_uninhabited_enum_tag, UninitEnumTag => const_eval_uninit_enum_tag, UninitStr => const_eval_uninit_str, Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool, @@ -760,7 +763,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | InvalidMetaSliceTooLarge { .. } | InvalidMetaTooLarge { .. } | DanglingPtrUseAfterFree { .. } - | DanglingPtrOutOfBounds { .. } => {} + | DanglingPtrOutOfBounds { .. } + | UninhabitedEnumTag => {} } } } @@ -835,7 +839,9 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { rustc_middle::error::middle_adjust_for_foreign_abi_error } InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized, - InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local, + InvalidProgramInfo::ConstPropNonsense => { + panic!("We had const-prop nonsense, this should never be printed") + } } } fn add_args<G: EmissionGuarantee>( @@ -846,7 +852,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> { match self { InvalidProgramInfo::TooGeneric | InvalidProgramInfo::AlreadyReported(_) - | InvalidProgramInfo::UninitUnsizedLocal => {} + | InvalidProgramInfo::ConstPropNonsense => {} InvalidProgramInfo::Layout(e) => { let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler); for (name, val) in diag.args() { diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index dd7a1fcc165..98e853dc4d9 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)?; } @@ -420,8 +420,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if cast_ty_field.is_zst() { continue; } - let src_field = self.operand_field(src, i)?; - let dst_field = self.place_field(dest, i)?; + let src_field = self.project_field(src, i)?; + let dst_field = self.project_field(dest, i)?; if src_field.layout.ty == cast_ty_field.ty { self.copy_op(&src_field, &dst_field, /*allow_transmute*/ false)?; } else { diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index f23a455c2ca..6c35fb01a93 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -1,11 +1,11 @@ //! Functions for reading and writing discriminants of multi-variant layouts (enums and generators). -use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt}; +use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout}; 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() { - throw_ub!(UninhabitedEnumVariantWritten) + 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 @@ -47,7 +51,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let size = tag_layout.size(self); let tag_val = size.truncate(discr_val); - let tag_dest = self.place_field(dest, tag_field)?; + let tag_dest = self.project_field(dest, tag_field)?; self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?; } abi::Variants::Multiple { @@ -78,7 +82,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &niche_start_val, )?; // Write result. - let niche_dest = self.place_field(dest, tag_field)?; + let niche_dest = self.project_field(dest, tag_field)?; self.write_immediate(*tag_val, &niche_dest)?; } } @@ -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>, - ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> { - trace!("read_discriminant_value {:#?}", op.layout); + op: &impl Readable<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, VariantIdx> { + 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,21 +109,23 @@ 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 } => { - let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) { - Some(discr) => { - // This type actually has discriminants. - assert_eq!(discr.ty, discr_layout.ty); - Scalar::from_uint(discr.val, discr_layout.size) + // Do some extra checks on enums. + if ty.is_enum() { + // Hilariously, `Single` is used even for 0-variant enums. + // (See https://github.com/rust-lang/rust/issues/89765). + if matches!(ty.kind(), ty::Adt(def, ..) if def.variants().is_empty()) { + throw_ub!(UninhabitedEnumVariantRead(index)) } - None => { - // On a type without actual discriminants, variant is 0. - assert_eq!(index.as_u32(), 0); - Scalar::from_uint(index.as_u32(), discr_layout.size) + // 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() { + throw_ub!(UninhabitedEnumVariantRead(index)) } - }; - return Ok((discr, index)); + } + return Ok(index); } Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => { (tag, tag_encoding, tag_field) @@ -138,13 +145,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?; // Read tag and sanity-check `tag_layout`. - let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?; + let tag_val = self.read_immediate(&self.project_field(op, tag_field)?)?; assert_eq!(tag_layout.size, tag_val.layout.size); assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed()); trace!("tag value: {}", tag_val); // Figure out which discriminant and variant this corresponds to. - Ok(match *tag_encoding { + let index = match *tag_encoding { TagEncoding::Direct => { let scalar = tag_val.to_scalar(); // Generate a specific error if `tag_val` is not an integer. @@ -160,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) } @@ -172,7 +179,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?; // Return the cast value, and the index. - (discr_val, index.0) + index.0 } TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => { let tag_val = tag_val.to_scalar(); @@ -214,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 { @@ -230,7 +233,32 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Compute the size of the scalar we need to return. // No need to cast, because the variant index directly serves as discriminant and is // encoded in the tag. - (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant) + variant + } + }; + // 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() { + throw_ub!(UninhabitedEnumVariantRead(index)) + } + Ok(index) + } + + pub fn discriminant_for_variant( + &self, + layout: TyAndLayout<'tcx>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Scalar<M::Provenance>> { + let discr_layout = self.layout_of(layout.ty.discriminant_ty(*self.tcx))?; + Ok(match layout.ty.discriminant_for_variant(*self.tcx, variant) { + Some(discr) => { + // This type actually has discriminants. + assert_eq!(discr.ty, discr_layout.ty); + Scalar::from_uint(discr.val, discr_layout.size) + } + None => { + // On a type without actual discriminants, variant is 0. + assert_eq!(variant.as_u32(), 0); + Scalar::from_uint(variant.as_u32(), discr_layout.size) } }) } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 04e046fbda3..3ac6f07e8b7 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -958,7 +958,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } else { self.param_env }; - let param_env = param_env.with_const(); let val = self.ctfe_query(span, |tcx| tcx.eval_to_allocation_raw(param_env.and(gid)))?; self.raw_const_to_mplace(val) } @@ -1014,9 +1013,12 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.place { - Place::Local { frame, local } => { + Place::Local { frame, local, offset } => { let mut allocs = Vec::new(); - write!(fmt, "{:?}", local)?; + write!(fmt, "{local:?}")?; + if let Some(offset) = offset { + write!(fmt, "+{:#x}", offset.bytes())?; + } if frame != self.ecx.frame_idx() { write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?; } @@ -1032,7 +1034,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug fmt, " by {} ref {:?}:", match mplace.meta { - MemPlaceMeta::Meta(meta) => format!(" meta({:?})", meta), + MemPlaceMeta::Meta(meta) => format!(" meta({meta:?})"), MemPlaceMeta::None => String::new(), }, mplace.ptr, @@ -1040,13 +1042,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug allocs.extend(mplace.ptr.provenance.map(Provenance::get_alloc_id)); } LocalValue::Live(Operand::Immediate(Immediate::Scalar(val))) => { - write!(fmt, " {:?}", val)?; + write!(fmt, " {val:?}")?; if let Scalar::Ptr(ptr, _size) = val { allocs.push(ptr.provenance.get_alloc_id()); } } LocalValue::Live(Operand::Immediate(Immediate::ScalarPair(val1, val2))) => { - write!(fmt, " ({:?}, {:?})", val1, val2)?; + write!(fmt, " ({val1:?}, {val2:?})")?; if let Scalar::Ptr(ptr, _size) = val1 { allocs.push(ptr.provenance.get_alloc_id()); } @@ -1062,7 +1064,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug Some(alloc_id) => { write!(fmt, "by ref {:?}: {:?}", mplace.ptr, self.ecx.dump_alloc(alloc_id)) } - ptr => write!(fmt, " integral by ref: {:?}", ptr), + ptr => write!(fmt, " integral by ref: {ptr:?}"), }, } } diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index 107e5bec614..910c3ca5d0a 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -164,82 +164,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory &self.ecx } - fn visit_aggregate( - &mut self, - mplace: &MPlaceTy<'tcx>, - fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>, - ) -> InterpResult<'tcx> { - // We want to walk the aggregate to look for references to intern. While doing that we - // also need to take special care of interior mutability. - // - // As an optimization, however, if the allocation does not contain any references: we don't - // need to do the walk. It can be costly for big arrays for example (e.g. issue #93215). - let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> { - // ZSTs cannot contain pointers, we can avoid the interning walk. - if mplace.layout.is_zst() { - return Ok(false); - } - - // Now, check whether this allocation could contain references. - // - // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like - // to avoid could be expensive: on the potentially larger types, arrays and slices, - // rather than on all aggregates unconditionally. - if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) { - let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else { - // We do the walk if we can't determine the size of the mplace: we may be - // dealing with extern types here in the future. - return Ok(true); - }; - - // If there is no provenance in this allocation, it does not contain references - // that point to another allocation, and we can avoid the interning walk. - if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? { - if !alloc.has_provenance() { - return Ok(false); - } - } else { - // We're encountering a ZST here, and can avoid the walk as well. - return Ok(false); - } - } - - // In the general case, we do the walk. - Ok(true) - }; - - // If this allocation contains no references to intern, we avoid the potentially costly - // walk. - // - // We can do this before the checks for interior mutability below, because only references - // are relevant in that situation, and we're checking if there are any here. - if !is_walk_needed(mplace)? { - return Ok(()); - } - - if let Some(def) = mplace.layout.ty.ty_adt_def() { - if def.is_unsafe_cell() { - // We are crossing over an `UnsafeCell`, we can mutate again. This means that - // References we encounter inside here are interned as pointing to mutable - // allocations. - // Remember the `old` value to handle nested `UnsafeCell`. - let old = std::mem::replace(&mut self.inside_unsafe_cell, true); - let walked = self.walk_aggregate(mplace, fields); - self.inside_unsafe_cell = old; - return walked; - } - } - - self.walk_aggregate(mplace, fields) - } - fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> { // Handle Reference types, as these are the only types with provenance supported by const eval. // Raw pointers (and boxes) are handled by the `leftover_allocations` logic. 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. @@ -315,7 +246,63 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory } Ok(()) } else { - // Not a reference -- proceed recursively. + // Not a reference. Check if we want to recurse. + let is_walk_needed = |mplace: &MPlaceTy<'tcx>| -> InterpResult<'tcx, bool> { + // ZSTs cannot contain pointers, we can avoid the interning walk. + if mplace.layout.is_zst() { + return Ok(false); + } + + // Now, check whether this allocation could contain references. + // + // Note, this check may sometimes not be cheap, so we only do it when the walk we'd like + // to avoid could be expensive: on the potentially larger types, arrays and slices, + // rather than on all aggregates unconditionally. + if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) { + let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else { + // We do the walk if we can't determine the size of the mplace: we may be + // dealing with extern types here in the future. + return Ok(true); + }; + + // If there is no provenance in this allocation, it does not contain references + // that point to another allocation, and we can avoid the interning walk. + if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? { + if !alloc.has_provenance() { + return Ok(false); + } + } else { + // We're encountering a ZST here, and can avoid the walk as well. + return Ok(false); + } + } + + // In the general case, we do the walk. + Ok(true) + }; + + // If this allocation contains no references to intern, we avoid the potentially costly + // walk. + // + // We can do this before the checks for interior mutability below, because only references + // are relevant in that situation, and we're checking if there are any here. + if !is_walk_needed(mplace)? { + return Ok(()); + } + + if let Some(def) = mplace.layout.ty.ty_adt_def() { + if def.is_unsafe_cell() { + // We are crossing over an `UnsafeCell`, we can mutate again. This means that + // References we encounter inside here are interned as pointing to mutable + // allocations. + // Remember the `old` value to handle nested `UnsafeCell`. + let old = std::mem::replace(&mut self.inside_unsafe_cell, true); + let walked = self.walk_value(mplace); + self.inside_unsafe_cell = old; + return walked; + } + } + self.walk_value(mplace) } } @@ -371,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 { @@ -477,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 04cae23f852..be7c14f33c2 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -226,8 +226,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } sym::discriminant_value => { let place = self.deref_operand(&args[0])?; - let discr_val = self.read_discriminant(&place.into())?.0; - self.write_scalar(discr_val, dest)?; + let variant = self.read_discriminant(&place)?; + let discr = self.discriminant_for_variant(place.layout, variant)?; + self.write_scalar(discr, dest)?; } sym::exact_div => { let l = self.read_immediate(&args[0])?; @@ -393,17 +394,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // For *all* intrinsics we first check `is_uninhabited` to give a more specific // error message. _ if layout.abi.is_uninhabited() => format!( - "aborted execution: attempted to instantiate uninhabited type `{}`", - ty + "aborted execution: attempted to instantiate uninhabited type `{ty}`" ), ValidityRequirement::Inhabited => bug!("handled earlier"), ValidityRequirement::Zero => format!( - "aborted execution: attempted to zero-initialize type `{}`, which is invalid", - ty + "aborted execution: attempted to zero-initialize type `{ty}`, which is invalid" ), ValidityRequirement::UninitMitigated0x01Fill => format!( - "aborted execution: attempted to leave type `{}` uninitialized, which is invalid", - ty + "aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid" ), ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"), }; @@ -419,19 +417,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(input_len, dest_len, "Return vector length must match input length"); assert!( index < dest_len, - "Index `{}` must be in bounds of vector with length {}", - index, - dest_len + "Index `{index}` must be in bounds of vector with length {dest_len}" ); for i in 0..dest_len { - let place = self.mplace_index(&dest, i)?; + let place = self.project_index(&dest, i)?; let value = if i == index { elem.clone() } else { - self.mplace_index(&input, i)?.into() + 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 => { @@ -439,12 +435,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (input, input_len) = self.operand_to_simd(&args[0])?; assert!( index < input_len, - "index `{}` must be in bounds of vector with length {}", - index, - input_len + "index `{index}` must be in bounds of vector with length {input_len}" ); self.copy_op( - &self.mplace_index(&input, index)?.into(), + &self.project_index(&input, index)?, dest, /*allow_transmute*/ false, )?; @@ -609,7 +603,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { count: &OpTy<'tcx, <M as Machine<'mir, '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), @@ -621,8 +615,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) } @@ -635,9 +629,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 c4fe293bfac..948bec7464a 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.mplace_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.mplace_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.mplace_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/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 7b44a20ef03..02d022a2252 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -53,7 +53,7 @@ impl<T: fmt::Display> fmt::Display for MemoryKind<T> { match self { MemoryKind::Stack => write!(f, "stack variable"), MemoryKind::CallerLocation => write!(f, "caller location"), - MemoryKind::Machine(m) => write!(f, "{}", m), + MemoryKind::Machine(m) => write!(f, "{m}"), } } } @@ -907,7 +907,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, match self.ecx.memory.alloc_map.get(id) { Some((kind, alloc)) => { // normal alloc - write!(fmt, " ({}, ", kind)?; + write!(fmt, " ({kind}, ")?; write_allocation_track_relocs( &mut *fmt, *self.ecx.tcx, diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index f657f954f9c..b0b553c45d4 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -24,11 +24,12 @@ 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}; -pub use self::visitor::{MutValueVisitor, Value, ValueVisitor}; +pub use self::visitor::ValueVisitor; pub(crate) use self::intrinsics::eval_nullary_intrinsic; use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index da45fdca1a1..6e57a56b445 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -1,6 +1,8 @@ //! Functions concerning immediate values and operands, and reading from operands. //! All high-level functions to read from memory work on operands as sources. +use std::assert_matches::assert_matches; + use either::{Either, Left, Right}; use rustc_hir::def::Namespace; @@ -13,8 +15,8 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size}; use super::{ alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId, - InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer, - Provenance, Scalar, + InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, + Projectable, Provenance, Scalar, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -31,7 +33,7 @@ pub enum Immediate<Prov: Provenance = AllocId> { /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are /// `Scalar::Initialized`). ScalarPair(Scalar<Prov>, Scalar<Prov>), - /// A value of fully uninitialized memory. Can have and size and layout. + /// A value of fully uninitialized memory. Can have arbitrary size and layout. Uninit, } @@ -178,20 +180,6 @@ impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> 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<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> { #[inline(always)] fn from(val: ImmTy<'tcx, Prov>) -> Self { @@ -240,43 +228,126 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { let int = self.to_scalar().assert_int(); ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) } + + /// Compute the "sub-immediate" that is located within the `base` at the given offset with the + /// given layout. + // Not called `offset` to avoid confusion with the trait method. + fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self { + // This makes several assumptions about what layouts we will encounter; we match what + // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). + let inner_val: Immediate<_> = match (**self, self.layout.abi) { + // if the entire value is uninit, then so is the field (can happen in ConstProp) + (Immediate::Uninit, _) => Immediate::Uninit, + // the field contains no information, can be left uninit + _ if layout.is_zst() => Immediate::Uninit, + // some fieldless enum variants can have non-zero size but still `Aggregate` ABI... try + // to detect those here and also give them no data + _ if matches!(layout.abi, Abi::Aggregate { .. }) + && matches!(&layout.fields, abi::FieldsShape::Arbitrary { offsets, .. } if offsets.len() == 0) => + { + Immediate::Uninit + } + // the field covers the entire type + _ if layout.size == self.layout.size => { + assert_eq!(offset.bytes(), 0); + assert!( + match (self.layout.abi, layout.abi) { + (Abi::Scalar(..), Abi::Scalar(..)) => true, + (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, + _ => false, + }, + "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}", + self.layout.ty, + layout.ty, + self.layout.abi, + layout.abi, + ); + **self + } + // extract fields from types with `ScalarPair` ABI + (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { + assert!(matches!(layout.abi, Abi::Scalar(..))); + Immediate::from(if offset.bytes() == 0 { + debug_assert_eq!(layout.size, a.size(cx)); + a_val + } else { + debug_assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi)); + debug_assert_eq!(layout.size, b.size(cx)); + b_val + }) + } + // everything else is a bug + _ => bug!("invalid field access on immediate {}, layout {:#?}", self, self.layout), + }; + + ImmTy::from_immediate(inner_val, layout) + } +} + +impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'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<M::Provenance>> { + assert!(self.layout.is_sized()); // unsized ImmTy can only exist temporarily and should never reach this here + Ok(MemPlaceMeta::None) + } + + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta<Prov>, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert_matches!(meta, MemPlaceMeta::None); // we can't store this anywhere anyway + Ok(self.offset_(offset, layout, cx)) + } + + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone().into()) + } } impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { - pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - if self.layout.is_unsized() { - if matches!(self.op, Operand::Immediate(Immediate::Uninit)) { - // Uninit unsized places shouldn't occur. In the interpreter we have them - // temporarily for unsized arguments before their value is put in; in ConstProp they - // remain uninit and this code can actually be reached. - throw_inval!(UninitUnsizedLocal); + // Provided as inherent method since it doesn't need the `ecx` of `Projectable::meta`. + pub fn meta(&self) -> InterpResult<'tcx, MemPlaceMeta<Prov>> { + Ok(if self.layout.is_unsized() { + if matches!(self.op, Operand::Immediate(_)) { + // Unsized immediate OpTy cannot occur. We create a MemPlace for all unsized locals during argument passing. + // However, ConstProp doesn't do that, so we can run into this nonsense situation. + throw_inval!(ConstPropNonsense); } // There are no unsized immediates. - self.assert_mem_place().len(cx) + self.assert_mem_place().meta } else { - match self.layout.fields { - abi::FieldsShape::Array { count, .. } => Ok(count), - _ => bug!("len not supported on sized type {:?}", self.layout.ty), - } - } + MemPlaceMeta::None + }) } +} - /// Replace the layout of this operand. There's basically no sanity check that this makes sense, - /// you better know what you are doing! If this is an immediate, applying the wrong layout can - /// not just lead to invalid data, it can actually *shift the data around* since the offsets of - /// a ScalarPair are entirely determined by the layout, not the data. - pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self { - assert_eq!( - self.layout.size, layout.size, - "transmuting with a size change, that doesn't seem right" - ); - OpTy { layout, ..*self } +impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> { + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout } - /// Offset the operand in memory (if possible) and change its metadata. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub(super) fn offset_with_meta( + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> { + self.meta() + } + + fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta<Prov>, @@ -286,28 +357,43 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { match self.as_mplace_or_imm() { Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()), Right(imm) => { - assert!( - matches!(*imm, Immediate::Uninit), - "Scalar/ScalarPair cannot be offset into" - ); assert!(!meta.has_meta()); // no place to store metadata here // Every part of an uninit is uninit. - Ok(ImmTy::uninit(layout).into()) + Ok(imm.offset(offset, layout, cx)?.into()) } } } - /// Offset the operand in memory (if possible). - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub fn offset( + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, - offset: Size, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - assert!(layout.is_sized()); - self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone()) + } +} + +pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { + fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>>; +} + +impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for OpTy<'tcx, Prov> { + #[inline(always)] + fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, 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<MPlaceTy<'tcx, Prov>, 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<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> { + Right(self.clone()) } } @@ -383,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<MPlaceTy<'tcx, M::Provenance>, 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), @@ -403,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) { @@ -422,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<M::Provenance>> { Ok(self.read_immediate(op)?.to_scalar()) } @@ -433,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<Option<M::Provenance>>> { 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) } @@ -497,18 +593,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Every place can be read from, so we can turn them into an operand. /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this /// will never actually read from memory. - #[inline(always)] pub fn place_to_op( &self, place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let op = match **place { - Place::Ptr(mplace) => Operand::Indirect(mplace), - Place::Local { frame, local } => { - *self.local_to_op(&self.stack()[frame], local, None)? + match place.as_mplace_or_local() { + Left(mplace) => Ok(mplace.into()), + Right((frame, local, offset)) => { + let base = self.local_to_op(&self.stack()[frame], local, None)?; + let mut field = if let Some(offset) = offset { + // This got offset. We can be sure that the field is sized. + base.offset(offset, place.layout, self)? + } else { + assert_eq!(place.layout, base.layout); + // Unsized cases are possible here since an unsized local will be a + // `Place::Local` until the first projection calls `place_to_op` to extract the + // underlying mplace. + base + }; + field.align = Some(place.align); + Ok(field) } - }; - Ok(OpTy { op, layout: place.layout, align: Some(place.align) }) + } } /// Evaluate a place with the goal of reading from it. This lets us sometimes @@ -525,7 +631,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { - op = self.operand_projection(&op, elem)? + op = self.project(&op, elem)? } trace!("eval_place_to_op: got {:?}", *op); @@ -592,10 +698,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ty::ConstKind::Unevaluated(uv) => { let instance = self.resolve(uv.def, uv.args)?; let cid = GlobalId { instance, promoted: None }; - self.ctfe_query(span, |tcx| { - tcx.eval_to_valtree(self.param_env.with_const().and(cid)) - })? - .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}")) + self.ctfe_query(span, |tcx| tcx.eval_to_valtree(self.param_env.and(cid)))? + .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}")) } ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => { span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {val:?}") diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index e04764636cc..eb064578067 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -24,8 +24,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { debug_assert_eq!( Ty::new_tup(self.tcx.tcx, &[ty, self.tcx.types.bool]), dest.layout.ty, - "type mismatch for result of {:?}", - op, + "type mismatch for result of {op:?}", ); // Write the result to `dest`. if let Abi::ScalarPair(..) = dest.layout.abi { @@ -38,9 +37,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to // do a component-wise write here. This code path is slower than the above because // `place_field` will have to `force_allocate` locals here. - let val_field = self.place_field(&dest, 0)?; + let val_field = self.project_field(dest, 0)?; self.write_scalar(val, &val_field)?; - let overflowed_field = self.place_field(&dest, 1)?; + let overflowed_field = self.project_field(dest, 1)?; self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?; } Ok(()) @@ -56,7 +55,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?; - assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op); + assert_eq!(ty, dest.layout.ty, "type mismatch for result of {op:?}"); self.write_scalar(val, dest) } } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index a9b2b43f1e6..96a960118ce 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -2,11 +2,14 @@ //! into a place. //! All high-level functions to write to memory work on places as destinations. +use std::assert_matches::assert_matches; + use either::{Either, Left, Right}; use rustc_ast::Mutability; use rustc_index::IndexSlice; use rustc_middle::mir; +use rustc_middle::mir::interpret::PointerArithmetic; use rustc_middle::ty; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; @@ -15,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, Provenance, Scalar, + Pointer, Projectable, Provenance, Readable, Scalar, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -44,6 +47,27 @@ impl<Prov: Provenance> MemPlaceMeta<Prov> { Self::None => false, } } + + pub(crate) fn len<'tcx>( + &self, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, u64> { + if layout.is_unsized() { + // We need to consult `meta` metadata + match layout.ty.kind() { + ty::Slice(..) | ty::Str => self.unwrap_meta().to_target_usize(cx), + _ => bug!("len not supported on unsized type {:?}", layout.ty), + } + } else { + // Go through the layout. There are lots of types that support a length, + // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) + match layout.fields { + abi::FieldsShape::Array { count, .. } => Ok(count), + _ => bug!("len not supported on sized type {:?}", layout.ty), + } + } + } } #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -57,7 +81,7 @@ pub struct MemPlace<Prov: Provenance = AllocId> { } /// 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<Prov>, pub layout: TyAndLayout<'tcx>, @@ -68,14 +92,26 @@ pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> { pub align: Align, } +impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> { + type Target = MemPlace<Prov>; + #[inline(always)] + fn deref(&self) -> &MemPlace<Prov> { + &self.mplace + } +} + #[derive(Copy, Clone, Debug)] pub enum Place<Prov: Provenance = AllocId> { /// A place referring to a value allocated in the `Memory` system. Ptr(MemPlace<Prov>), - /// To support alloc-free locals, we are able to write directly to a local. + /// To support alloc-free locals, we are able to write directly to a local. The offset indicates + /// where in the local this place is located; if it is `None`, no projection has been applied. + /// Such projections are meaningful even if the offset is 0, since they can change layouts. /// (Without that optimization, we'd just always be a `MemPlace`.) - Local { frame: usize, local: mir::Local }, + /// Note that this only stores the frame index, not the thread this frame belongs to -- that is + /// implicit. This means a `Place` must never be moved across interpreter thread boundaries! + Local { frame: usize, local: mir::Local, offset: Option<Size> }, } #[derive(Clone, Debug)] @@ -97,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<Prov>; - #[inline(always)] - fn deref(&self) -> &MemPlace<Prov> { - &self.mplace - } -} - impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> { #[inline(always)] fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self { @@ -112,26 +140,17 @@ impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> 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<Prov: Provenance> MemPlace<Prov> { #[inline(always)] pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self { MemPlace { ptr, meta: MemPlaceMeta::None } } + #[inline(always)] + pub fn from_ptr_with_meta(ptr: Pointer<Option<Prov>>, meta: MemPlaceMeta<Prov>) -> Self { + MemPlace { ptr, meta } + } + /// Adjust the provenance of the main pointer (metadata is unaffected). pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self { MemPlace { ptr: self.ptr.map_provenance(f), ..self } @@ -150,7 +169,8 @@ impl<Prov: Provenance> MemPlace<Prov> { } #[inline] - pub(super) fn offset_with_meta<'tcx>( + // Not called `offset_with_meta` to avoid confusion with the trait method. + fn offset_with_meta_<'tcx>( self, offset: Size, meta: MemPlaceMeta<Prov>, @@ -164,19 +184,6 @@ impl<Prov: Provenance> MemPlace<Prov> { } } -impl<Prov: Provenance> Place<Prov> { - /// Asserts that this points to some local variable. - /// Returns the frame idx and the variable idx. - #[inline] - #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) - pub fn assert_local(&self) -> (usize, mir::Local) { - match self { - Place::Local { frame, local } => (*frame, *local), - _ => bug!("assert_local: expected Place::Local, got {:?}", self), - } - } -} - impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { /// Produces a MemPlace that works for ZST but nothing else. /// Conceptually this is a new allocation, but it doesn't actually create an allocation so you @@ -189,11 +196,39 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align } } - /// Offset the place in memory and change its metadata. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! #[inline] - pub(crate) fn offset_with_meta( + pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self { + MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } + } + + #[inline] + pub fn from_aligned_ptr_with_meta( + ptr: Pointer<Option<Prov>>, + layout: TyAndLayout<'tcx>, + meta: MemPlaceMeta<Prov>, + ) -> Self { + MPlaceTy { + mplace: MemPlace::from_ptr_with_meta(ptr, meta), + layout, + align: layout.align.abi, + } + } +} + +impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'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<M::Provenance>> { + Ok(self.meta) + } + + fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta<Prov>, @@ -201,58 +236,65 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> { cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MPlaceTy { - mplace: self.mplace.offset_with_meta(offset, meta, cx)?, + mplace: self.mplace.offset_with_meta_(offset, meta, cx)?, align: self.align.restrict_for_offset(offset), layout, }) } - /// Offset the place in memory. - /// - /// This can go wrong very easily if you give the wrong layout for the new place! - pub fn offset( + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, - offset: Size, - layout: TyAndLayout<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, Self> { - assert!(layout.is_sized()); - self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + Ok(self.clone().into()) } +} - #[inline] - pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self { - MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } +impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout } - #[inline] - pub fn from_aligned_ptr_with_meta( - ptr: Pointer<Option<Prov>>, - layout: TyAndLayout<'tcx>, - meta: MemPlaceMeta<Prov>, - ) -> Self { - let mut mplace = MemPlace::from_ptr(ptr); - mplace.meta = meta; - - MPlaceTy { mplace, layout, align: layout.align.abi } + fn meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> { + ecx.place_meta(self) } - #[inline] - pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { - if self.layout.is_unsized() { - // We need to consult `meta` metadata - match self.layout.ty.kind() { - ty::Slice(..) | ty::Str => self.mplace.meta.unwrap_meta().to_target_usize(cx), - _ => bug!("len not supported on unsized type {:?}", self.layout.ty), - } - } else { - // Go through the layout. There are lots of types that support a length, - // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!) - match self.layout.fields { - abi::FieldsShape::Array { count, .. } => Ok(count), - _ => bug!("len not supported on sized type {:?}", self.layout.ty), + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta<Prov>, + 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) } } @@ -280,13 +322,15 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { } } -impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { +impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> { /// A place is either an mplace or some local. #[inline] - pub fn as_mplace_or_local(&self) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local)> { + pub fn as_mplace_or_local( + &self, + ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> { match **self { Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }), - Place::Local { frame, local } => Right((frame, local)), + Place::Local { frame, local, offset } => Right((frame, local, offset)), } } @@ -302,12 +346,74 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { } } +pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { + fn as_mplace_or_local( + &self, + ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, 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 as_mplace_or_local( + &self, + ) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)> + { + self.as_mplace_or_local() + .map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout)) + } + + #[inline(always)] + fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &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<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)> + { + Left(self.clone()) + } + + #[inline(always)] + fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + _ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>> { + Ok(self.clone()) + } +} + // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> where Prov: Provenance + 'static, M: Machine<'mir, 'tcx, Provenance = Prov>, { + /// Get the metadata of the given place. + pub(super) fn place_meta( + &self, + place: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>> { + if place.layout.is_unsized() { + // For `Place::Local`, the metadata is stored with the local, not the place. So we have + // to look that up first. + self.place_to_op(place)?.meta() + } else { + Ok(MemPlaceMeta::None) + } + } + /// Take a value, which represents a (thin or wide) reference, and make it a place. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. /// @@ -327,18 +433,16 @@ where Immediate::Uninit => throw_ub!(InvalidUninitBytes(None)), }; - let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta }; // `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced; // we hence can't call `size_and_align_of` since that asserts more validity than we want. - let align = layout.align.abi; - Ok(MPlaceTy { mplace, layout, align }) + Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta)) } /// Take an operand, representing a pointer, and dereference it to a place. #[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); @@ -348,7 +452,7 @@ where } let mplace = self.ref_to_mplace(&val)?; - self.check_mplace(mplace)?; + self.check_mplace(&mplace)?; Ok(mplace) } @@ -379,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)); @@ -422,7 +526,7 @@ where local: mir::Local, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let layout = self.layout_of_local(&self.stack()[frame], local, None)?; - let place = Place::Local { frame, local }; + let place = Place::Local { frame, local, offset: None }; Ok(PlaceTy { place, layout, align: layout.align.abi }) } @@ -430,13 +534,13 @@ where /// place; for reading, a more efficient alternative is `eval_place_to_op`. #[instrument(skip(self), level = "debug")] pub fn eval_place( - &mut self, + &self, mir_place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { - place = self.place_projection(&place, elem)? + place = self.project(&place, elem)? } trace!("{:?}", self.dump_place(place.place)); @@ -463,13 +567,13 @@ where pub fn write_immediate( &mut self, src: Immediate<M::Provenance>, - 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(()) @@ -480,7 +584,7 @@ where pub fn write_scalar( &mut self, val: impl Into<Scalar<M::Provenance>>, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_immediate(Immediate::Scalar(val.into()), dest) } @@ -490,7 +594,7 @@ where pub fn write_pointer( &mut self, ptr: impl Into<Pointer<Option<M::Provenance>>>, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { self.write_scalar(Scalar::from_maybe_pointer(ptr.into(), self), dest) } @@ -501,32 +605,63 @@ where fn write_immediate_no_validate( &mut self, src: Immediate<M::Provenance>, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - assert!(dest.layout.is_sized(), "Cannot write unsized 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 } => { - match M::access_local_mut(self, frame, local)? { - Operand::Immediate(local) => { - // Local can be updated in-place. - *local = src; - return Ok(()); - } - Operand::Indirect(mplace) => { - // The local is in memory, go on below. - *mplace + 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. + dest.force_mplace(self)? + } else { + match M::access_local_mut(self, frame, local)? { + Operand::Immediate(local_val) => { + // Local can be updated in-place. + *local_val = src; + // Double-check that the value we are storing and the local fit to each other. + // (*After* doing the update for borrow checker reasons.) + if cfg!(debug_assertions) { + let local_layout = + self.layout_of_local(&self.stack()[frame], local, None)?; + match (src, local_layout.abi) { + (Immediate::Scalar(scalar), Abi::Scalar(s)) => { + assert_eq!(scalar.size(), s.size(self)) + } + ( + Immediate::ScalarPair(a_val, b_val), + Abi::ScalarPair(a, b), + ) => { + assert_eq!(a_val.size(), a.size(self)); + assert_eq!(b_val.size(), b.size(self)); + } + (Immediate::Uninit, _) => {} + (src, abi) => { + bug!( + "value {src:?} cannot be written into local with type {} (ABI {abi:?})", + local_layout.ty + ) + } + }; + } + return Ok(()); + } + Operand::Indirect(mplace) => { + // The local is in memory, go on below. + 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. @@ -590,18 +725,29 @@ 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)) => { - match M::access_local_mut(self, frame, local)? { - Operand::Immediate(local) => { - *local = Immediate::Uninit; - return Ok(()); - } - Operand::Indirect(mplace) => { - // The local is in memory, go on below. - MPlaceTy { mplace: *mplace, layout: dest.layout, align: dest.align } + 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`. + dest.force_mplace(self)? + } else { + match M::access_local_mut(self, frame, local)? { + Operand::Immediate(local) => { + *local = Immediate::Uninit; + return Ok(()); + } + Operand::Indirect(mplace) => { + // The local is in memory, go on below. + MPlaceTy { mplace: *mplace, layout, align } + } } } } @@ -620,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(()) @@ -641,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, ); } @@ -667,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) @@ -682,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, ) @@ -694,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") }; @@ -728,8 +874,8 @@ where place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let mplace = match place.place { - Place::Local { frame, local } => { - match M::access_local_mut(self, frame, local)? { + Place::Local { frame, local, offset } => { + let whole_local = match M::access_local_mut(self, frame, local)? { &mut Operand::Immediate(local_val) => { // We need to make an allocation. @@ -742,10 +888,11 @@ where throw_unsup_format!("unsized locals are not supported"); } let mplace = *self.allocate(local_layout, MemoryKind::Stack)?; + // Preserve old value. (As an optimization, we can skip this if it was uninit.) if !matches!(local_val, Immediate::Uninit) { - // Preserve old value. (As an optimization, we can skip this if it was uninit.) - // We don't have to validate as we can assume the local - // was already valid for its type. + // We don't have to validate as we can assume the local was already + // valid for its type. We must not use any part of `place` here, that + // could be a projection to a part of the local! self.write_immediate_to_mplace_no_validate( local_val, local_layout, @@ -753,18 +900,25 @@ where mplace, )?; } - // Now we can call `access_mut` again, asserting it goes well, - // and actually overwrite things. + // Now we can call `access_mut` again, asserting it goes well, and actually + // overwrite things. This points to the entire allocation, not just the part + // the place refers to, i.e. we do this before we apply `offset`. *M::access_local_mut(self, frame, local).unwrap() = Operand::Indirect(mplace); mplace } &mut Operand::Indirect(mplace) => mplace, // this already was an indirect local + }; + if let Some(offset) = offset { + whole_local.offset_with_meta_(offset, MemPlaceMeta::None, self)? + } else { + // Preserve wide place metadata, do not call `offset`. + whole_local } } Place::Ptr(mplace) => mplace, }; - // Return with the original layout, so that the caller can go on + // Return with the original layout and align, so that the caller can go on Ok(MPlaceTy { mplace, layout: place.layout, align: place.align }) } @@ -806,10 +960,10 @@ where operands: &IndexSlice<FieldIdx, mir::Operand<'tcx>>, 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.place_downcast(&dest, variant_index)?; + let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } _ => (FIRST_VARIANT, dest.clone(), None), @@ -819,11 +973,11 @@ where } for (field_index, operand) in operands.iter_enumerated() { let field_index = active_field_index.unwrap_or(field_index); - let field_dest = self.place_field(&variant_dest, field_index.as_usize())?; + let field_dest = self.project_field(&variant_dest, field_index.as_usize())?; 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( @@ -859,22 +1013,24 @@ where Ok((mplace, vtable)) } - /// Turn an operand with a `dyn* Trait` type into an operand with the actual dynamic type. - /// Aso returns the vtable. - pub(super) fn unpack_dyn_star( + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. + /// Also returns the vtable. + pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>( &self, - op: &OpTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, (OpTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> { + val: &P, + ) -> InterpResult<'tcx, (P, Pointer<Option<M::Provenance>>)> { assert!( - matches!(op.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), "`unpack_dyn_star` only makes sense on `dyn*` types" ); - let data = self.operand_field(&op, 0)?; - let vtable = self.operand_field(&op, 1)?; - let vtable = self.read_pointer(&vtable)?; + let data = self.project_field(val, 0)?; + let vtable = self.project_field(val, 1)?; + let vtable = self.read_pointer(&vtable.to_op(self)?)?; let (ty, _) = self.get_ptr_vtable(vtable)?; let layout = self.layout_of(ty)?; - let data = data.transmute(layout); + // `data` is already the right thing but has the wrong type. So we transmute it, by + // projecting with offset 0. + let data = data.transmute(layout, self)?; Ok((data, vtable)) } } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index d7d31fe1887..bce43aedb69 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -7,18 +7,70 @@ //! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually //! implement the logic on OpTy, and MPlaceTy calls that. -use either::{Left, Right}; - use rustc_middle::mir; use rustc_middle::ty; -use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::Ty; -use rustc_target::abi::{self, Abi, VariantIdx}; +use rustc_middle::ty::TyCtxt; +use rustc_target::abi::HasDataLayout; +use rustc_target::abi::Size; +use rustc_target::abi::{self, VariantIdx}; + +use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar}; -use super::{ - ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy, - Provenance, Scalar, -}; +/// A thing that we can project into, and that has a layout. +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<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, MemPlaceMeta<M::Provenance>>; + + fn len<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, u64> { + self.meta(ecx)?.len(self.layout(), ecx) + } + + /// Offset the value by the given amount, replacing the layout and metadata. + fn offset_with_meta( + &self, + offset: Size, + meta: MemPlaceMeta<Prov>, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self>; + + fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(layout.is_sized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } + + fn transmute( + &self, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert_eq!(self.layout().size, layout.size); + self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, cx) + } + + /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for + /// reading from this thing. + fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; +} // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M> @@ -26,167 +78,83 @@ where Prov: Provenance + 'static, M: Machine<'mir, 'tcx, Provenance = Prov>, { - //# Field access - /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is /// always possible without allocating, so it can take `&self`. Also return the field's layout. - /// This supports both struct and array fields. + /// This supports both struct and array fields, but not slices! /// /// This also works for arrays, but then the `usize` index type is restricting. /// For indexing into arrays, use `mplace_index`. - pub fn mplace_field( + pub fn project_field<P: Projectable<'tcx, M::Provenance>>( &self, - base: &MPlaceTy<'tcx, M::Provenance>, + base: &P, field: usize, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - let offset = base.layout.fields.offset(field); - let field_layout = base.layout.field(self, field); + ) -> InterpResult<'tcx, P> { + // Slices nominally have length 0, so they will panic somewhere in `fields.offset`. + debug_assert!( + !matches!(base.layout().ty.kind(), ty::Slice(..)), + "`field` projection called on a slice -- call `index` projection instead" + ); + let offset = base.layout().fields.offset(field); + let field_layout = base.layout().field(self, field); // Offset may need adjustment for unsized fields. let (meta, offset) = if field_layout.is_unsized() { + if base.layout().is_sized() { + // An unsized field of a sized type? Sure... + // But const-prop actually feeds us such nonsense MIR! + throw_inval!(ConstPropNonsense); + } + let base_meta = base.meta(self)?; // Re-use parent metadata to determine dynamic field layout. // With custom DSTS, this *will* execute user-defined code, but the same // happens at run-time so that's okay. - match self.size_and_align_of(&base.meta, &field_layout)? { - Some((_, align)) => (base.meta, offset.align_to(align)), + match self.size_and_align_of(&base_meta, &field_layout)? { + Some((_, align)) => (base_meta, offset.align_to(align)), None => { // For unsized types with an extern type tail we perform no adjustments. // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend. - assert!(matches!(base.meta, MemPlaceMeta::None)); - (base.meta, offset) + assert!(matches!(base_meta, MemPlaceMeta::None)); + (base_meta, offset) } } } else { - // base.meta could be present; we might be accessing a sized field of an unsized + // base_meta could be present; we might be accessing a sized field of an unsized // struct. (MemPlaceMeta::None, offset) }; - // We do not look at `base.layout.align` nor `field_layout.align`, unlike - // codegen -- mostly to see if we can get away with that base.offset_with_meta(offset, meta, field_layout, self) } - /// Gets the place of a field inside the place, and also the field's type. - /// Just a convenience function, but used quite a bit. - /// This is the only projection that might have a side-effect: We cannot project - /// into the field of a local `ScalarPair`, we have to first allocate it. - pub fn place_field( - &mut self, - base: &PlaceTy<'tcx, M::Provenance>, - field: usize, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - // FIXME: We could try to be smarter and avoid allocation for fields that span the - // entire place. - let base = self.force_allocation(base)?; - Ok(self.mplace_field(&base, field)?.into()) - } - - pub fn operand_field( + /// Downcasting to an enum variant. + pub fn project_downcast<P: Projectable<'tcx, M::Provenance>>( &self, - base: &OpTy<'tcx, M::Provenance>, - field: usize, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - let base = match base.as_mplace_or_imm() { - Left(ref mplace) => { - // We can reuse the mplace field computation logic for indirect operands. - let field = self.mplace_field(mplace, field)?; - return Ok(field.into()); - } - Right(value) => value, - }; - - let field_layout = base.layout.field(self, field); - let offset = base.layout.fields.offset(field); - // This makes several assumptions about what layouts we will encounter; we match what - // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`). - let field_val: Immediate<_> = match (*base, base.layout.abi) { - // if the entire value is uninit, then so is the field (can happen in ConstProp) - (Immediate::Uninit, _) => Immediate::Uninit, - // the field contains no information, can be left uninit - _ if field_layout.is_zst() => Immediate::Uninit, - // the field covers the entire type - _ if field_layout.size == base.layout.size => { - assert!(match (base.layout.abi, field_layout.abi) { - (Abi::Scalar(..), Abi::Scalar(..)) => true, - (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true, - _ => false, - }); - assert!(offset.bytes() == 0); - *base - } - // extract fields from types with `ScalarPair` ABI - (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => { - assert!(matches!(field_layout.abi, Abi::Scalar(..))); - Immediate::from(if offset.bytes() == 0 { - debug_assert_eq!(field_layout.size, a.size(self)); - a_val - } else { - debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi)); - debug_assert_eq!(field_layout.size, b.size(self)); - b_val - }) - } - // everything else is a bug - _ => span_bug!( - self.cur_span(), - "invalid field access on immediate {}, layout {:#?}", - base, - base.layout - ), - }; - - Ok(ImmTy::from_immediate(field_val, field_layout).into()) - } - - //# Downcasting - - pub fn mplace_downcast( - &self, - base: &MPlaceTy<'tcx, M::Provenance>, + base: &P, variant: VariantIdx, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + ) -> InterpResult<'tcx, P> { + assert!(!base.meta(self)?.has_meta()); // Downcasts only change the layout. // (In particular, no check about whether this is even the active variant -- that's by design, // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.) - assert!(!base.meta.has_meta()); - let mut base = *base; - base.layout = base.layout.for_variant(self, variant); - Ok(base) - } - - pub fn place_downcast( - &self, - base: &PlaceTy<'tcx, M::Provenance>, - variant: VariantIdx, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - // Downcast just changes the layout - let mut base = base.clone(); - base.layout = base.layout.for_variant(self, variant); - Ok(base) - } - - pub fn operand_downcast( - &self, - base: &OpTy<'tcx, M::Provenance>, - variant: VariantIdx, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // Downcast just changes the layout - let mut base = base.clone(); - base.layout = base.layout.for_variant(self, variant); - Ok(base) + // So we just "offset" by 0. + let layout = base.layout().for_variant(self, variant); + if layout.abi.is_uninhabited() { + // `read_discriminant` should have excluded uninhabited variants... but ConstProp calls + // us on dead code. + throw_inval!(ConstPropNonsense) + } + // This cannot be `transmute` as variants *can* have a smaller size than the entire enum. + base.offset(Size::ZERO, layout, self) } - //# Slice indexing - - #[inline(always)] - pub fn operand_index( + /// Compute the offset and field layout for accessing the given index. + pub fn project_index<P: Projectable<'tcx, M::Provenance>>( &self, - base: &OpTy<'tcx, M::Provenance>, + base: &P, index: u64, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + ) -> InterpResult<'tcx, P> { // Not using the layout method because we want to compute on u64 - match base.layout.fields { + let (offset, field_layout) = match base.layout().fields { abi::FieldsShape::Array { stride, count: _ } => { // `count` is nonsense for slices, use the dynamic length instead. let len = base.len(self)?; @@ -196,63 +164,26 @@ where } let offset = stride * index; // `Size` multiplication // All fields have the same layout. - let field_layout = base.layout.field(self, 0); - base.offset(offset, field_layout, self) + let field_layout = base.layout().field(self, 0); + (offset, field_layout) } _ => span_bug!( self.cur_span(), "`mplace_index` called on non-array type {:?}", - base.layout.ty + base.layout().ty ), - } - } - - /// Iterates over all fields of an array. Much more efficient than doing the - /// same by repeatedly calling `operand_index`. - pub fn operand_array_fields<'a>( - &self, - base: &'a OpTy<'tcx, Prov>, - ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Prov>>> + 'a> { - let len = base.len(self)?; // also asserts that we have a type where this makes sense - let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { - span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); }; - let field_layout = base.layout.field(self, 0); - let dl = &self.tcx.data_layout; - // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) - } - - /// Index into an array. - pub fn mplace_index( - &self, - base: &MPlaceTy<'tcx, M::Provenance>, - index: u64, - ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { - Ok(self.operand_index(&base.into(), index)?.assert_mem_place()) - } - pub fn place_index( - &mut self, - base: &PlaceTy<'tcx, M::Provenance>, - index: u64, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - // There's not a lot we can do here, since we cannot have a place to a part of a local. If - // we are accessing the only element of a 1-element array, it's still the entire local... - // that doesn't seem worth it. - let base = self.force_allocation(base)?; - Ok(self.mplace_index(&base, index)?.into()) + base.offset(offset, field_layout, self) } - //# ConstantIndex support - - fn operand_constant_index( + fn project_constant_index<P: Projectable<'tcx, M::Provenance>>( &self, - base: &OpTy<'tcx, M::Provenance>, + base: &P, offset: u64, min_length: u64, from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + ) -> InterpResult<'tcx, P> { let n = base.len(self)?; if n < min_length { // This can only be reached in ConstProp and non-rustc-MIR. @@ -267,32 +198,38 @@ where offset }; - self.operand_index(base, index) + self.project_index(base, index) } - fn place_constant_index( - &mut self, - base: &PlaceTy<'tcx, M::Provenance>, - offset: u64, - min_length: u64, - from_end: bool, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let base = self.force_allocation(base)?; - Ok(self - .operand_constant_index(&base.into(), offset, min_length, from_end)? - .assert_mem_place() - .into()) + /// 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<'tcx, M::Provenance>>( + &self, + base: &'a P, + ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, P>> + 'a> + where + 'tcx: 'a, + { + let abi::FieldsShape::Array { stride, .. } = base.layout().fields else { + span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); + }; + let len = base.len(self)?; + let field_layout = base.layout().field(self, 0); + let tcx: TyCtxt<'tcx> = *self.tcx; + // `Size` multiplication + Ok((0..len).map(move |i| { + base.offset_with_meta(stride * i, MemPlaceMeta::None, field_layout, &tcx) + })) } - //# Subslicing - - fn operand_subslice( + /// Subslicing + fn project_subslice<P: Projectable<'tcx, M::Provenance>>( &self, - base: &OpTy<'tcx, M::Provenance>, + base: &P, from: u64, to: u64, from_end: bool, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + ) -> InterpResult<'tcx, P> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let actual_to = if from_end { if from.checked_add(to).map_or(true, |to| to > len) { @@ -306,16 +243,20 @@ where // Not using layout method because that works with usize, and does not work with slices // (that have count 0 in their layout). - let from_offset = match base.layout.fields { + let from_offset = match base.layout().fields { abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked _ => { - span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout) + span_bug!( + self.cur_span(), + "unexpected layout of index access: {:#?}", + base.layout() + ) } }; // Compute meta and new layout let inner_len = actual_to.checked_sub(from).unwrap(); - let (meta, ty) = match base.layout.ty.kind() { + let (meta, ty) = match base.layout().ty.kind() { // It is not nice to match on the type, but that seems to be the only way to // implement this. ty::Array(inner, _) => { @@ -323,85 +264,43 @@ where } ty::Slice(..) => { let len = Scalar::from_target_usize(inner_len, self); - (MemPlaceMeta::Meta(len), base.layout.ty) + (MemPlaceMeta::Meta(len), base.layout().ty) } _ => { - span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty) + span_bug!( + self.cur_span(), + "cannot subslice non-array type: `{:?}`", + base.layout().ty + ) } }; let layout = self.layout_of(ty)?; - base.offset_with_meta(from_offset, meta, layout, self) - } - - pub fn place_subslice( - &mut self, - base: &PlaceTy<'tcx, M::Provenance>, - from: u64, - to: u64, - from_end: bool, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let base = self.force_allocation(base)?; - Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into()) - } - - //# Applying a general projection - /// Projects into a place. - #[instrument(skip(self), level = "trace")] - pub fn place_projection( - &mut self, - base: &PlaceTy<'tcx, M::Provenance>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - use rustc_middle::mir::ProjectionElem::*; - Ok(match proj_elem { - OpaqueCast(ty) => { - let mut place = base.clone(); - place.layout = self.layout_of(ty)?; - place - } - Field(field, _) => self.place_field(base, field.index())?, - Downcast(_, variant) => self.place_downcast(base, variant)?, - Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), - Index(local) => { - let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.local_to_op(self.frame(), local, Some(layout))?; - let n = self.read_target_usize(&n)?; - self.place_index(base, n)? - } - ConstantIndex { offset, min_length, from_end } => { - self.place_constant_index(base, offset, min_length, from_end)? - } - Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?, - }) + base.offset_with_meta(from_offset, meta, layout, self) } + /// Applying a general projection #[instrument(skip(self), level = "trace")] - pub fn operand_projection( - &self, - base: &OpTy<'tcx, M::Provenance>, - proj_elem: mir::PlaceElem<'tcx>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + pub fn project<P>(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpResult<'tcx, P> + where + P: Projectable<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>> + std::fmt::Debug, + { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { - OpaqueCast(ty) => { - let mut op = base.clone(); - op.layout = self.layout_of(ty)?; - op - } - Field(field, _) => self.operand_field(base, field.index())?, - Downcast(_, variant) => self.operand_downcast(base, variant)?, - Deref => self.deref_operand(base)?.into(), + OpaqueCast(ty) => base.transmute(self.layout_of(ty)?, self)?, + Field(field, _) => self.project_field(base, field.index())?, + Downcast(_, variant) => self.project_downcast(base, variant)?, + Deref => self.deref_operand(&base.to_op(self)?)?.into(), Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; let n = self.local_to_op(self.frame(), local, Some(layout))?; let n = self.read_target_usize(&n)?; - self.operand_index(base, n)? + self.project_index(base, n)? } ConstantIndex { offset, min_length, from_end } => { - self.operand_constant_index(base, offset, min_length, from_end)? + self.project_constant_index(base, offset, min_length, from_end)? } - Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?, + Subslice { from, to, from_end } => self.project_subslice(base, from, to, from_end)?, }) } } diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 619da8abb7d..ead172f04a3 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -8,7 +8,7 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::ty::layout::LayoutOf; -use super::{ImmTy, InterpCx, Machine}; +use super::{ImmTy, InterpCx, Machine, Projectable}; use crate::util; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -178,7 +178,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // The operand always has the same type as the result. let val = self.read_immediate(&self.eval_operand(operand, Some(dest.layout))?)?; let val = self.unary_op(un_op, &val)?; - assert_eq!(val.layout, dest.layout, "layout mismatch for result of {:?}", un_op); + assert_eq!(val.layout, dest.layout, "layout mismatch for result of {un_op:?}"); self.write_immediate(*val, &dest)?; } @@ -197,8 +197,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.get_place_alloc_mut(&dest)?; } else { // Write the src to the first element. - let first = self.mplace_field(&dest, 0)?; - self.copy_op(&src, &first.into(), /*allow_transmute*/ false)?; + let first = self.project_index(&dest, 0)?; + 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 @@ -208,13 +208,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let rest_ptr = first_ptr.offset(elem_size, self)?; // For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as // that place might be more aligned than its type mandates (a `u8` array could - // be 4-aligned if it sits at the right spot in a struct). Instead we use - // `first.layout.align`, i.e., the alignment given by the type. + // be 4-aligned if it sits at the right spot in a struct). We have to also factor + // in element size. self.mem_copy_repeatedly( first_ptr, - first.align, + dest.align, rest_ptr, - first.layout.align.abi, + dest.align.restrict_for_offset(elem_size), elem_size, length - 1, /*nonoverlapping:*/ true, @@ -302,8 +302,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Discriminant(place) => { let op = self.eval_place_to_op(place, None)?; - let discr_val = self.read_discriminant(&op)?.0; - self.write_scalar(discr_val, &dest)?; + let variant = self.read_discriminant(&op)?; + let discr = self.discriminant_for_variant(op.layout, variant)?; + self.write_scalar(discr, &dest)?; } } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 7964c6be008..d0191ea978a 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -60,13 +60,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } pub fn fn_arg_field( - &mut self, + &self, arg: &FnArg<'tcx, M::Provenance>, field: usize, ) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> { Ok(match arg { - FnArg::Copy(op) => FnArg::Copy(self.operand_field(op, field)?), - FnArg::InPlace(place) => FnArg::InPlace(self.place_field(place, field)?), + FnArg::Copy(op) => FnArg::Copy(self.project_field(op, field)?), + FnArg::InPlace(place) => FnArg::InPlace(self.project_field(place, field)?), }) } @@ -239,7 +239,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Evaluate the arguments of a function call pub(super) fn eval_fn_call_arguments( - &mut self, + &self, ops: &[mir::Operand<'tcx>], ) -> InterpResult<'tcx, Vec<FnArg<'tcx, M::Provenance>>> { ops.iter() @@ -382,12 +382,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // This all has to be in memory, there are no immediate unsized values. let src = caller_arg_copy.assert_mem_place(); // The destination cannot be one of these "spread args". - let (dest_frame, dest_local) = callee_arg.assert_local(); + let (dest_frame, dest_local, dest_offset) = callee_arg + .as_mplace_or_local() + .right() + .expect("callee fn arguments must be locals"); // We are just initializing things, so there can't be anything here yet. assert!(matches!( *self.local_to_op(&self.stack()[dest_frame], dest_local, None)?, Operand::Immediate(Immediate::Uninit) )); + assert_eq!(dest_offset, None); // Allocate enough memory to hold `src`. let Some((size, align)) = self.size_and_align_of_mplace(&src)? else { span_bug!( @@ -595,7 +599,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if Some(local) == body.spread_arg { // Must be a tuple for i in 0..dest.layout.fields.count() { - let dest = self.place_field(&dest, i)?; + let dest = self.project_field(&dest, i)?; let callee_abi = callee_args_abis.next().unwrap(); self.pass_argument(&mut caller_args, callee_abi, &dest)?; } @@ -630,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. } @@ -677,7 +681,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Not there yet, search for the only non-ZST field. let mut non_zst_field = None; for i in 0..receiver.layout.fields.count() { - let field = self.operand_field(&receiver, i)?; + let field = self.project_field(&receiver, i)?; let zst = field.layout.is_zst() && field.layout.align.abi.bytes() == 1; if !zst { @@ -703,12 +707,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?; + let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?; let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; if dyn_trait != data.principal() { throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch); } - let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one (vptr, dyn_ty, recv.ptr) } else { @@ -836,7 +839,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } ty::Dynamic(_, _, ty::DynStar) => { // Dropping a `dyn*`. Need to find actual drop fn. - self.unpack_dyn_star(&place.into())?.0.assert_mem_place() + self.unpack_dyn_star(&place)?.0 } _ => { debug_assert_eq!( diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 21c655988a0..aee95f70bc2 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -29,7 +29,7 @@ use std::hash::Hash; use super::UndefinedBehaviorInfo::*; use super::{ AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, - Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor, + Machine, MemPlaceMeta, OpTy, Pointer, Projectable, Scalar, ValueVisitor, }; macro_rules! throw_validation_failure { @@ -136,19 +136,19 @@ pub struct RefTracking<T, PATH = ()> { pub todo: Vec<(T, PATH)>, } -impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> { +impl<T: Clone + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH> { 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. @@ -164,14 +164,14 @@ fn write_path(out: &mut String, path: &[PathElem]) { for elem in path.iter() { match elem { - Field(name) => write!(out, ".{}", name), + Field(name) => write!(out, ".{name}"), EnumTag => write!(out, ".<enum-tag>"), - Variant(name) => write!(out, ".<enum-variant({})>", name), + Variant(name) => write!(out, ".<enum-variant({name})>"), GeneratorTag => write!(out, ".<generator-tag>"), GeneratorState(idx) => write!(out, ".<generator-state({})>", idx.index()), - CapturedVar(name) => write!(out, ".<captured-var({})>", name), - TupleElem(idx) => write!(out, ".{}", idx), - ArrayElem(idx) => write!(out, "[{}]", idx), + CapturedVar(name) => write!(out, ".<captured-var({name})>"), + TupleElem(idx) => write!(out, ".{idx}"), + ArrayElem(idx) => write!(out, "[{idx}]"), // `.<deref>` does not match Rust syntax, but it is more readable for long paths -- and // some of the other items here also are not Rust syntax. Actually we can't // even use the usual syntax because we are just showing the projections, @@ -462,6 +462,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' /// Check if this is a value of primitive type, and if yes check the validity of the value /// at that type. Return `true` if the type is indeed primitive. + /// + /// Note that not all of these have `FieldsShape::Primitive`, e.g. wide references. fn try_visit_primitive( &mut self, value: &OpTy<'tcx, M::Provenance>, @@ -660,10 +662,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> InvalidTag(val) => InvalidEnumTag { value: format!("{val:x}"), }, - + UninhabitedEnumVariantRead(_) => UninhabitedEnumTag, InvalidUninitBytes(None) => UninitEnumTag, - ) - .1) + )) }) } @@ -733,60 +734,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } } - // Recursively walk the value at its type. - self.walk_value(op)?; - - // *After* all of this, check the ABI. We need to check the ABI to handle - // types like `NonNull` where the `Scalar` info is more restrictive than what - // the fields say (`rustc_layout_scalar_valid_range_start`). - // But in most cases, this will just propagate what the fields say, - // and then we want the error to point at the field -- so, first recurse, - // then check ABI. - // - // FIXME: We could avoid some redundant checks here. For newtypes wrapping - // scalars, we do the same check on every "level" (e.g., first we check - // MyNewtype and then the scalar in there). - match op.layout.abi { - Abi::Uninhabited => { - let ty = op.layout.ty; - throw_validation_failure!(self.path, UninhabitedVal { ty }); - } - Abi::Scalar(scalar_layout) => { - if !scalar_layout.is_uninit_valid() { - // There is something to check here. - let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?; - self.visit_scalar(scalar, scalar_layout)?; - } - } - Abi::ScalarPair(a_layout, b_layout) => { - // We can only proceed if *both* scalars need to be initialized. - // FIXME: find a way to also check ScalarPair when one side can be uninit but - // the other must be init. - if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { - let (a, b) = - self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair(); - self.visit_scalar(a, a_layout)?; - self.visit_scalar(b, b_layout)?; - } - } - Abi::Vector { .. } => { - // No checks here, we assume layout computation gets this right. - // (This is harder to check since Miri does not represent these as `Immediate`. We - // also cannot use field projections since this might be a newtype around a vector.) - } - Abi::Aggregate { .. } => { - // Nothing to do. - } - } - - Ok(()) - } - - fn visit_aggregate( - &mut self, - op: &OpTy<'tcx, M::Provenance>, - fields: impl Iterator<Item = InterpResult<'tcx, Self::V>>, - ) -> InterpResult<'tcx> { + // Recursively walk the value at its type. Apply optimizations for some large types. match op.layout.ty.kind() { ty::Str => { let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate @@ -874,12 +822,58 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // ZST type, so either validation fails for all elements or none. ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => { // Validate just the first element (if any). - self.walk_aggregate(op, fields.take(1))? + if op.len(self.ecx)? > 0 { + self.visit_field(op, 0, &self.ecx.project_index(op, 0)?)?; + } } _ => { - self.walk_aggregate(op, fields)? // default handler + self.walk_value(op)?; // default handler } } + + // *After* all of this, check the ABI. We need to check the ABI to handle + // types like `NonNull` where the `Scalar` info is more restrictive than what + // the fields say (`rustc_layout_scalar_valid_range_start`). + // But in most cases, this will just propagate what the fields say, + // and then we want the error to point at the field -- so, first recurse, + // then check ABI. + // + // FIXME: We could avoid some redundant checks here. For newtypes wrapping + // scalars, we do the same check on every "level" (e.g., first we check + // MyNewtype and then the scalar in there). + match op.layout.abi { + Abi::Uninhabited => { + let ty = op.layout.ty; + throw_validation_failure!(self.path, UninhabitedVal { ty }); + } + Abi::Scalar(scalar_layout) => { + if !scalar_layout.is_uninit_valid() { + // There is something to check here. + let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?; + self.visit_scalar(scalar, scalar_layout)?; + } + } + Abi::ScalarPair(a_layout, b_layout) => { + // We can only proceed if *both* scalars need to be initialized. + // FIXME: find a way to also check ScalarPair when one side can be uninit but + // the other must be init. + if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() { + let (a, b) = + self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair(); + self.visit_scalar(a, a_layout)?; + self.visit_scalar(b, b_layout)?; + } + } + Abi::Vector { .. } => { + // No checks here, we assume layout computation gets this right. + // (This is harder to check since Miri does not represent these as `Immediate`. We + // also cannot use field projections since this might be a newtype around a vector.) + } + Abi::Aggregate { .. } => { + // Nothing to do. + } + } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 879ae198f7e..531e2bd3ee0 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -1,544 +1,202 @@ //! Visitor for a run-time value with a given layout: Traverse enums, structs and other compound //! types until we arrive at the leaves, with custom handling for primitive types. +use rustc_index::IndexVec; use rustc_middle::mir::interpret::InterpResult; use rustc_middle::ty; -use rustc_middle::ty::layout::TyAndLayout; +use rustc_target::abi::FieldIdx; use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; use std::num::NonZeroUsize; -use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy}; +use super::{InterpCx, MPlaceTy, Machine, Projectable}; -/// A thing that we can project into, and that has a layout. -/// This wouldn't have to depend on `Machine` but with the current type inference, -/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). -pub trait Value<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - /// Gets this value's layout. - fn layout(&self) -> TyAndLayout<'tcx>; +/// 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<'tcx, M::Provenance> + From<MPlaceTy<'tcx, M::Provenance>>; - /// Makes this into an `OpTy`, in a cheap way that is good for reading. - fn to_op_for_read( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; - - /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. - fn to_op_for_proj( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - self.to_op_for_read(ecx) - } - - /// Creates this from an `OpTy`. - /// - /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; - - /// Projects to the given enum variant. - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self>; - - /// Projects to the n-th field. - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self>; -} - -/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout. -/// This wouldn't have to depend on `Machine` but with the current type inference, -/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). -pub trait ValueMut<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - /// Gets this value's layout. - fn layout(&self) -> TyAndLayout<'tcx>; - - /// Makes this into an `OpTy`, in a cheap way that is good for reading. - fn to_op_for_read( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; - - /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. - fn to_op_for_proj( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>>; - - /// Creates this from an `OpTy`. - /// - /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self; - - /// Projects to the given enum variant. - fn project_downcast( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self>; - - /// Projects to the n-th field. - fn project_field( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self>; -} - -// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we -// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.) -// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this -// double-impl, that would barely make the code shorter, if at all.) - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::Provenance> { - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - op.clone() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.operand_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.operand_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for OpTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) - } - - #[inline(always)] - fn to_op_for_proj( - &self, - _ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.clone()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - op.clone() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.operand_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.operand_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands. - op.assert_mem_place() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for MPlaceTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - _ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) - } - - #[inline(always)] - fn to_op_for_proj( - &self, - _ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - Ok(self.into()) - } - - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands. - op.assert_mem_place() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.mplace_field(self, field) - } -} - -impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> - for PlaceTy<'tcx, M::Provenance> -{ - #[inline(always)] - fn layout(&self) -> TyAndLayout<'tcx> { - self.layout - } - - #[inline(always)] - fn to_op_for_read( - &self, - ecx: &InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // No need for `force_allocation` since we are just going to read from this. - ecx.place_to_op(self) - } + /// The visitor must have an `InterpCx` in it. + fn ecx(&self) -> &InterpCx<'mir, 'tcx, M>; + /// `read_discriminant` can be hooked for better error messages. #[inline(always)] - fn to_op_for_proj( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { - // We `force_allocation` here so that `from_op` below can work. - Ok(ecx.force_allocation(self)?.into()) + fn read_discriminant(&mut self, v: &Self::V) -> InterpResult<'tcx, VariantIdx> { + Ok(self.ecx().read_discriminant(&v.to_op(self.ecx())?)?) } - #[inline(always)] - fn from_op(op: &OpTy<'tcx, M::Provenance>) -> Self { - // assert is justified because our `to_op` only ever produces `Indirect` operands. - op.assert_mem_place().into() - } - - #[inline(always)] - fn project_downcast( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - variant: VariantIdx, - ) -> InterpResult<'tcx, Self> { - ecx.place_downcast(self, variant) - } - - #[inline(always)] - fn project_field( - &self, - ecx: &mut InterpCx<'mir, 'tcx, M>, - field: usize, - ) -> InterpResult<'tcx, Self> { - ecx.place_field(self, field) - } -} - -macro_rules! make_value_visitor { - ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => { - /// How to traverse a value and what to do when we are at the leaves. - pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - type V: $value_trait<'mir, 'tcx, M>; - - /// The visitor must have an `InterpCx` in it. - fn ecx(&$($mutability)? self) - -> &$($mutability)? InterpCx<'mir, 'tcx, M>; - - /// `read_discriminant` can be hooked for better error messages. - #[inline(always)] - fn read_discriminant( - &mut self, - op: &OpTy<'tcx, M::Provenance>, - ) -> InterpResult<'tcx, VariantIdx> { - Ok(self.ecx().read_discriminant(op)?.1) - } - - // Recursive actions, ready to be overloaded. - /// Visits the given value, dispatching as appropriate to more specialized visitors. - #[inline(always)] - fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx> - { - self.walk_value(v) - } - /// Visits the given value as a union. No automatic recursion can happen here. - #[inline(always)] - fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> - { - Ok(()) - } - /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. - /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the - /// pointee type is the actual `T`. - #[inline(always)] - fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> - { - Ok(()) + /// This function provides the chance to reorder the order in which fields are visited for + /// `FieldsShape::Aggregate`: The order of fields will be + /// `(0..num_fields).map(aggregate_field_order)`. + /// + /// The default means we iterate in source declaration order; alternative this can do an inverse + /// lookup in `memory_index` to use memory field order instead. + #[inline(always)] + fn aggregate_field_order(_memory_index: &IndexVec<FieldIdx, u32>, idx: usize) -> usize { + idx + } + + // Recursive actions, ready to be overloaded. + /// Visits the given value, dispatching as appropriate to more specialized visitors. + #[inline(always)] + fn visit_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { + self.walk_value(v) + } + /// Visits the given value as a union. No automatic recursion can happen here. + #[inline(always)] + fn visit_union(&mut self, _v: &Self::V, _fields: NonZeroUsize) -> InterpResult<'tcx> { + Ok(()) + } + /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into. + /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the + /// pointee type is the actual `T`. + #[inline(always)] + fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> { + Ok(()) + } + + /// Called each time we recurse down to a field of a "product-like" aggregate + /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) + /// and new (inner) value. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. + #[inline(always)] + fn visit_field( + &mut self, + _old_val: &Self::V, + _field: usize, + new_val: &Self::V, + ) -> InterpResult<'tcx> { + self.visit_value(new_val) + } + /// Called when recursing into an enum variant. + /// This gives the visitor the chance to track the stack of nested fields that + /// we are descending through. + #[inline(always)] + fn visit_variant( + &mut self, + _old_val: &Self::V, + _variant: VariantIdx, + new_val: &Self::V, + ) -> InterpResult<'tcx> { + self.visit_value(new_val) + } + + fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { + let ty = v.layout().ty; + trace!("walk_value: type: {ty}"); + + // Special treatment for special types, where the (static) layout is not sufficient. + match *ty.kind() { + // If it is a trait object, switch to the real type that was used to create it. + ty::Dynamic(_, _, ty::Dyn) => { + // Dyn types. This is unsized, and the actual dynamic type of the data is given by the + // vtable stored in the place metadata. + // unsized values are never immediate, so we can assert_mem_place + let op = v.to_op(self.ecx())?; + let dest = op.assert_mem_place(); + let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; + trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); + // recurse with the inner type + return self.visit_field(&v, 0, &inner_mplace.into()); } - /// Visits this value as an aggregate, you are getting an iterator yielding - /// all the fields (still in an `InterpResult`, you have to do error handling yourself). - /// Recurses into the fields. - #[inline(always)] - fn visit_aggregate( - &mut self, - v: &Self::V, - fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>, - ) -> InterpResult<'tcx> { - self.walk_aggregate(v, fields) + ty::Dynamic(_, _, ty::DynStar) => { + // DynStar types. Very different from a dyn type (but strangely part of the + // same variant in `TyKind`): These are pairs where the 2nd component is the + // vtable, and the first component is the data (which must be ptr-sized). + let data = self.ecx().unpack_dyn_star(v)?.0; + return self.visit_field(&v, 0, &data); } - - /// Called each time we recurse down to a field of a "product-like" aggregate - /// (structs, tuples, arrays and the like, but not enums), passing in old (outer) - /// and new (inner) value. - /// This gives the visitor the chance to track the stack of nested fields that - /// we are descending through. - #[inline(always)] - fn visit_field( - &mut self, - _old_val: &Self::V, - _field: usize, - new_val: &Self::V, - ) -> InterpResult<'tcx> { - self.visit_value(new_val) + // Slices do not need special handling here: they have `Array` field + // placement with length 0, so we enter the `Array` case below which + // indirectly uses the metadata to determine the actual length. + + // However, `Box`... let's talk about `Box`. + ty::Adt(def, ..) if def.is_box() => { + // `Box` is a hybrid primitive-library-defined type that one the one hand is + // a dereferenceable pointer, on the other hand has *basically arbitrary + // user-defined layout* since the user controls the 'allocator' field. So it + // cannot be treated like a normal pointer, since it does not fit into an + // `Immediate`. Yeah, it is quite terrible. But many visitors want to do + // something with "all boxed pointers", so we handle this mess for them. + // + // When we hit a `Box`, we do not do the usual field recursion; instead, + // we (a) call `visit_box` on the pointer value, and (b) recurse on the + // allocator field. We also assert tons of things to ensure we do not miss + // any other fields. + + // `Box` has two fields: the pointer we care about, and the allocator. + assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); + let (unique_ptr, alloc) = + (self.ecx().project_field(v, 0)?, self.ecx().project_field(v, 1)?); + // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... + // (which means another 2 fields, the second of which is a `PhantomData`) + assert_eq!(unique_ptr.layout().fields.count(), 2); + let (nonnull_ptr, phantom) = ( + self.ecx().project_field(&unique_ptr, 0)?, + self.ecx().project_field(&unique_ptr, 1)?, + ); + assert!( + phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), + "2nd field of `Unique` should be PhantomData but is {:?}", + phantom.layout().ty, + ); + // ... that contains a `NonNull`... (gladly, only a single field here) + assert_eq!(nonnull_ptr.layout().fields.count(), 1); + let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr + // ... whose only field finally is a raw ptr we can dereference. + self.visit_box(&raw_ptr)?; + + // The second `Box` field is the allocator, which we recursively check for validity + // like in regular structs. + self.visit_field(v, 1, &alloc)?; + + // We visited all parts of this one. + return Ok(()); } - /// Called when recursing into an enum variant. - /// This gives the visitor the chance to track the stack of nested fields that - /// we are descending through. - #[inline(always)] - fn visit_variant( - &mut self, - _old_val: &Self::V, - _variant: VariantIdx, - new_val: &Self::V, - ) -> InterpResult<'tcx> { - self.visit_value(new_val) + _ => {} + }; + + // Visit the fields of this value. + match &v.layout().fields { + FieldsShape::Primitive => {} + &FieldsShape::Union(fields) => { + self.visit_union(v, fields)?; } - - // Default recursors. Not meant to be overloaded. - fn walk_aggregate( - &mut self, - v: &Self::V, - fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>, - ) -> InterpResult<'tcx> { - // Now iterate over it. - for (idx, field_val) in fields.enumerate() { - self.visit_field(v, idx, &field_val?)?; + FieldsShape::Arbitrary { offsets, memory_index } => { + for idx in 0..offsets.len() { + let idx = Self::aggregate_field_order(memory_index, idx); + let field = self.ecx().project_field(v, idx)?; + self.visit_field(v, idx, &field)?; } - Ok(()) } - fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> - { - let ty = v.layout().ty; - trace!("walk_value: type: {ty}"); - - // Special treatment for special types, where the (static) layout is not sufficient. - match *ty.kind() { - // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(_, _, ty::Dyn) => { - // Dyn types. This is unsized, and the actual dynamic type of the data is given by the - // vtable stored in the place metadata. - // unsized values are never immediate, so we can assert_mem_place - let op = v.to_op_for_read(self.ecx())?; - let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; - trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); - // recurse with the inner type - return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); - }, - ty::Dynamic(_, _, ty::DynStar) => { - // DynStar types. Very different from a dyn type (but strangely part of the - // same variant in `TyKind`): These are pairs where the 2nd component is the - // vtable, and the first component is the data (which must be ptr-sized). - let op = v.to_op_for_proj(self.ecx())?; - let data = self.ecx().unpack_dyn_star(&op)?.0; - return self.visit_field(&v, 0, &$value_trait::from_op(&data)); - } - // Slices do not need special handling here: they have `Array` field - // placement with length 0, so we enter the `Array` case below which - // indirectly uses the metadata to determine the actual length. - - // However, `Box`... let's talk about `Box`. - ty::Adt(def, ..) if def.is_box() => { - // `Box` is a hybrid primitive-library-defined type that one the one hand is - // a dereferenceable pointer, on the other hand has *basically arbitrary - // user-defined layout* since the user controls the 'allocator' field. So it - // cannot be treated like a normal pointer, since it does not fit into an - // `Immediate`. Yeah, it is quite terrible. But many visitors want to do - // something with "all boxed pointers", so we handle this mess for them. - // - // When we hit a `Box`, we do not do the usual `visit_aggregate`; instead, - // we (a) call `visit_box` on the pointer value, and (b) recurse on the - // allocator field. We also assert tons of things to ensure we do not miss - // any other fields. - - // `Box` has two fields: the pointer we care about, and the allocator. - assert_eq!(v.layout().fields.count(), 2, "`Box` must have exactly 2 fields"); - let (unique_ptr, alloc) = - (v.project_field(self.ecx(), 0)?, v.project_field(self.ecx(), 1)?); - // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`... - // (which means another 2 fields, the second of which is a `PhantomData`) - assert_eq!(unique_ptr.layout().fields.count(), 2); - let (nonnull_ptr, phantom) = ( - unique_ptr.project_field(self.ecx(), 0)?, - unique_ptr.project_field(self.ecx(), 1)?, - ); - assert!( - phantom.layout().ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()), - "2nd field of `Unique` should be PhantomData but is {:?}", - phantom.layout().ty, - ); - // ... that contains a `NonNull`... (gladly, only a single field here) - assert_eq!(nonnull_ptr.layout().fields.count(), 1); - let raw_ptr = nonnull_ptr.project_field(self.ecx(), 0)?; // the actual raw ptr - // ... whose only field finally is a raw ptr we can dereference. - self.visit_box(&raw_ptr)?; - - // The second `Box` field is the allocator, which we recursively check for validity - // like in regular structs. - self.visit_field(v, 1, &alloc)?; - - // We visited all parts of this one. - return Ok(()); - } - _ => {}, - }; - - // Visit the fields of this value. - match &v.layout().fields { - FieldsShape::Primitive => {} - &FieldsShape::Union(fields) => { - self.visit_union(v, fields)?; - } - FieldsShape::Arbitrary { offsets, .. } => { - // FIXME: We collect in a vec because otherwise there are lifetime - // errors: Projecting to a field needs access to `ecx`. - let fields: Vec<InterpResult<'tcx, Self::V>> = - (0..offsets.len()).map(|i| { - v.project_field(self.ecx(), i) - }) - .collect(); - self.visit_aggregate(v, fields.into_iter())?; - } - FieldsShape::Array { .. } => { - // Let's get an mplace (or immediate) first. - // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway. - let op = v.to_op_for_proj(self.ecx())?; - // Now we can go over all the fields. - // This uses the *run-time length*, i.e., if we are a slice, - // the dynamic info from the metadata is used. - let iter = self.ecx().operand_array_fields(&op)? - .map(|f| f.and_then(|f| { - Ok($value_trait::from_op(&f)) - })); - self.visit_aggregate(v, iter)?; - } + FieldsShape::Array { .. } => { + for (idx, field) in self.ecx().project_array_fields(v)?.enumerate() { + self.visit_field(v, idx, &field?)?; } + } + } - match v.layout().variants { - // If this is a multi-variant layout, find the right variant and proceed - // with *its* fields. - Variants::Multiple { .. } => { - let op = v.to_op_for_read(self.ecx())?; - let idx = self.read_discriminant(&op)?; - let inner = v.project_downcast(self.ecx(), idx)?; - trace!("walk_value: variant layout: {:#?}", inner.layout()); - // recurse with the inner type - self.visit_variant(v, idx, &inner) - } - // For single-variant layouts, we already did anything there is to do. - Variants::Single { .. } => Ok(()) - } + match v.layout().variants { + // If this is a multi-variant layout, find the right variant and proceed + // with *its* fields. + Variants::Multiple { .. } => { + let idx = self.read_discriminant(v)?; + // There are 3 cases where downcasts can turn a Scalar/ScalarPair into a different ABI which + // could be a problem for `ImmTy` (see layout_sanity_check): + // - variant.size == Size::ZERO: works fine because `ImmTy::offset` has a special case for + // zero-sized layouts. + // - variant.fields.count() == 0: works fine because `ImmTy::offset` has a special case for + // zero-field aggregates. + // - variant.abi.is_uninhabited(): triggers UB in `read_discriminant` so we never get here. + let inner = self.ecx().project_downcast(v, idx)?; + trace!("walk_value: variant layout: {:#?}", inner.layout()); + // recurse with the inner type + self.visit_variant(v, idx, &inner)?; } + // For single-variant layouts, we already did anything there is to do. + Variants::Single { .. } => {} } + + Ok(()) } } - -make_value_visitor!(ValueVisitor, Value,); -make_value_visitor!(MutValueVisitor, ValueMut, mut); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index d8b8fa927c7..ad5ffa6511f 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -8,6 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ImplSource, Obligation, ObligationCause}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_middle::ty::{TraitRef, TypeVisitableExt}; @@ -20,7 +21,7 @@ use std::mem; use std::ops::Deref; use super::ops::{self, NonConstOp, Status}; -use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; +use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; @@ -33,7 +34,7 @@ type QualifResults<'mir, 'tcx, Q> = pub struct Qualifs<'mir, 'tcx> { has_mut_interior: Option<QualifResults<'mir, 'tcx, HasMutInterior>>, needs_drop: Option<QualifResults<'mir, 'tcx, NeedsDrop>>, - needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>, + // needs_non_const_drop: Option<QualifResults<'mir, 'tcx, NeedsNonConstDrop>>, } impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { @@ -76,15 +77,17 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { local: Local, location: Location, ) -> bool { + // FIXME(effects) replace with `NeedsNonconstDrop` after const traits work again + /* let ty = ccx.body.local_decls[local].ty; - if !NeedsNonConstDrop::in_any_value_of_ty(ccx, ty) { + if !NeedsDrop::in_any_value_of_ty(ccx, ty) { return false; } let needs_non_const_drop = self.needs_non_const_drop.get_or_insert_with(|| { let ConstCx { tcx, body, .. } = *ccx; - FlowSensitiveAnalysis::new(NeedsNonConstDrop, ccx) + FlowSensitiveAnalysis::new(NeedsDrop, ccx) .into_engine(tcx, &body) .iterate_to_fixpoint() .into_results_cursor(&body) @@ -92,6 +95,9 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { needs_non_const_drop.seek_before_primary_effect(location); needs_non_const_drop.get().contains(local) + */ + + self.needs_drop(ccx, local, location) } /// Returns `true` if `local` is `HasMutInterior` at the given `Location`. @@ -766,7 +772,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }; match implsrc { - Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => { + Ok(Some(ImplSource::Param(ty::BoundConstness::ConstIfConst, _))) => { debug!( "const_trait_impl: provided {:?} via where-clause in {:?}", trait_ref, param_env @@ -774,7 +780,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { return; } // Closure: Fn{Once|Mut} - Ok(Some(ImplSource::Builtin(_))) + Ok(Some(ImplSource::Builtin(BuiltinImplSource::Misc, _))) if trait_ref.self_ty().is_closure() && tcx.fn_trait_kind_from_def_id(trait_id).is_some() => { @@ -797,16 +803,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Ok(Some(ImplSource::UserDefined(data))) => { let callee_name = tcx.item_name(callee); - if let Some(&did) = tcx - .associated_item_def_ids(data.impl_def_id) - .iter() - .find(|did| tcx.item_name(**did) == callee_name) - { - // using internal args is ok here, since this is only - // used for the `resolve` call below - fn_args = GenericArgs::identity_for_item(tcx, did); - callee = did; - } if let hir::Constness::NotConst = tcx.constness(data.impl_def_id) { self.check_op(ops::FnCallNonConst { @@ -819,6 +815,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { }); return; } + + if let Some(&did) = tcx + .associated_item_def_ids(data.impl_def_id) + .iter() + .find(|did| tcx.item_name(**did) == callee_name) + { + // using internal args is ok here, since this is only + // used for the `resolve` call below + fn_args = GenericArgs::identity_for_item(tcx, did); + callee = did; + } } _ if !tcx.is_const_fn_raw(callee) => { // At this point, it is only legal when the caller is in a trait @@ -995,8 +1002,9 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { let mut err_span = self.span; let ty_of_dropped_place = dropped_place.ty(self.body, self.tcx).ty; + // FIXME(effects) replace with `NeedsNonConstDrop` once we fix const traits let ty_needs_non_const_drop = - qualifs::NeedsNonConstDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place); + qualifs::NeedsDrop::in_any_value_of_ty(self.ccx, ty_of_dropped_place); debug!(?ty_of_dropped_place, ?ty_needs_non_const_drop); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 81337079af2..e785196c744 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -310,8 +310,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() { err.help(format!( - "add `#![feature({})]` to the crate attributes to enable", - feature, + "add `#![feature({feature})]` to the crate attributes to enable", )); } @@ -346,10 +345,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { err.help("const-stable functions can only call other const-stable functions"); } else if ccx.tcx.sess.is_nightly_build() { if let Some(feature) = feature { - err.help(format!( - "add `#![feature({})]` to the crate attributes to enable", - feature - )); + err.help(format!("add `#![feature({feature})]` to the crate attributes to enable")); } } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index 1f1640fd80a..e3377bd10c6 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -5,7 +5,7 @@ use rustc_span::{symbol::sym, Span}; use super::check::Qualifs; use super::ops::{self, NonConstOp}; -use super::qualifs::{NeedsNonConstDrop, Qualif}; +use super::qualifs::{NeedsDrop, Qualif}; use super::ConstCx; /// Returns `true` if we should use the more precise live drop checker that runs after drop @@ -82,7 +82,9 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { match &terminator.kind { mir::TerminatorKind::Drop { place: dropped_place, .. } => { let dropped_ty = dropped_place.ty(self.body, self.tcx).ty; - if !NeedsNonConstDrop::in_any_value_of_ty(self.ccx, dropped_ty) { + + // FIXME(effects) use `NeedsNonConstDrop` + if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) { // Instead of throwing a bug, we just return here. This is because we have to // run custom `const Drop` impls. return; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 0ef7ace6965..b152644a551 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -7,6 +7,7 @@ use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir; use rustc_middle::mir::*; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty}; use rustc_trait_selection::traits::{ self, ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, @@ -22,7 +23,8 @@ pub fn in_any_value_of_ty<'tcx>( ConstQualifs { has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty), needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty), - needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty), + // FIXME(effects) + needs_non_const_drop: NeedsDrop::in_any_value_of_ty(cx, ty), custom_eq: CustomEq::in_any_value_of_ty(cx, ty), tainted_by_errors, } @@ -172,7 +174,8 @@ impl Qualif for NeedsNonConstDrop { if !matches!( impl_src, - ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst) + ImplSource::Builtin(BuiltinImplSource::Misc, _) + | ImplSource::Param(ty::BoundConstness::ConstIfConst, _) ) { // If our const destruct candidate is not ConstDestruct or implied by the param env, // then it's bad diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index ea0d90dbd51..31effadd2c2 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -58,11 +58,10 @@ impl<'tcx> MirPass<'tcx> for Validator { .iterate_to_fixpoint() .into_results_cursor(body); - let mut checker = TypeChecker { + let mut cfg_checker = CfgChecker { when: &self.when, body, tcx, - param_env, mir_phase, unwind_edge_count: 0, reachable_blocks: traversal::reachable_as_bitset(body), @@ -70,13 +69,17 @@ impl<'tcx> MirPass<'tcx> for Validator { place_cache: FxHashSet::default(), value_cache: FxHashSet::default(), }; - checker.visit_body(body); - checker.check_cleanup_control_flow(); + cfg_checker.visit_body(body); + cfg_checker.check_cleanup_control_flow(); + + for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) { + cfg_checker.fail(location, msg); + } if let MirPhase::Runtime(_) = body.phase { if let ty::InstanceDef::Item(_) = body.source.instance { if body.has_free_regions() { - checker.fail( + cfg_checker.fail( Location::START, format!("Free regions in optimized {} MIR", body.phase.name()), ); @@ -86,11 +89,10 @@ impl<'tcx> MirPass<'tcx> for Validator { } } -struct TypeChecker<'a, 'tcx> { +struct CfgChecker<'a, 'tcx> { when: &'a str, body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, mir_phase: MirPhase, unwind_edge_count: usize, reachable_blocks: BitSet<BasicBlock>, @@ -99,7 +101,7 @@ struct TypeChecker<'a, 'tcx> { value_cache: FxHashSet<u128>, } -impl<'a, 'tcx> TypeChecker<'a, 'tcx> { +impl<'a, 'tcx> CfgChecker<'a, 'tcx> { #[track_caller] fn fail(&self, location: Location, msg: impl AsRef<str>) { let span = self.body.source_info(location).span; @@ -147,7 +149,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } } else { - self.fail(location, format!("encountered jump to invalid basic block {:?}", bb)) + self.fail(location, format!("encountered jump to invalid basic block {bb:?}")) } } @@ -220,8 +222,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.fail( Location { block: bb, statement_index: 0 }, format!( - "Cleanup control flow violation: Cycle involving edge {:?} -> {:?}", - bb, parent, + "Cleanup control flow violation: Cycle involving edge {bb:?} -> {parent:?}", ), ); break; @@ -248,35 +249,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { UnwindAction::Unreachable | UnwindAction::Terminate => (), } } - - /// Check if src can be assigned into dest. - /// This is not precise, it will accept some incorrect assignments. - fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool { - // Fast path before we normalize. - if src == dest { - // Equal types, all is good. - return true; - } - - // We sometimes have to use `defining_opaque_types` for subtyping - // to succeed here and figuring out how exactly that should work - // is annoying. It is harmless enough to just not validate anything - // in that case. We still check this after analysis as all opaque - // types have been revealed at this point. - if (src, dest).has_opaque_types() { - return true; - } - - crate::util::is_subtype(self.tcx, self.param_env, src, dest) - } } -impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) { if self.body.local_decls.get(local).is_none() { self.fail( location, - format!("local {:?} has no corresponding declaration in `body.local_decls`", local), + format!("local {local:?} has no corresponding declaration in `body.local_decls`"), ); } @@ -291,11 +271,280 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.storage_liveness.seek_after_primary_effect(location); let locals_with_storage = self.storage_liveness.get(); if !locals_with_storage.contains(local) { - self.fail(location, format!("use of local {:?}, which has no storage here", local)); + self.fail(location, format!("use of local {local:?}, which has no storage here")); } } } + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match &statement.kind { + StatementKind::Assign(box (dest, rvalue)) => { + // FIXME(JakobDegen): Check this for all rvalues, not just this one. + if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue { + // The sides of an assignment must not alias. Currently this just checks whether + // the places are identical. + if dest == src { + self.fail( + location, + "encountered `Assign` statement with overlapping memory", + ); + } + } + } + StatementKind::AscribeUserType(..) => { + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail( + location, + "`AscribeUserType` should have been removed after drop lowering phase", + ); + } + } + StatementKind::FakeRead(..) => { + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail( + location, + "`FakeRead` should have been removed after drop lowering phase", + ); + } + } + StatementKind::SetDiscriminant { .. } => { + if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); + } + } + StatementKind::Deinit(..) => { + if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + self.fail(location, "`Deinit`is not allowed until deaggregation"); + } + } + StatementKind::Retag(kind, _) => { + // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // DropsLowered`. However, this causes ICEs with generation of drop shims, which + // seem to fail to set their `MirPhase` correctly. + if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) { + self.fail(location, format!("explicit `{kind:?}` is forbidden")); + } + } + StatementKind::StorageLive(local) => { + // We check that the local is not live when entering a `StorageLive` for it. + // Technically, violating this restriction is only UB and not actually indicative + // of not well-formed MIR. This means that an optimization which turns MIR that + // already has UB into MIR that fails this check is not necessarily wrong. However, + // we have no such optimizations at the moment, and so we include this check anyway + // to help us catch bugs. If you happen to write an optimization that might cause + // this to incorrectly fire, feel free to remove this check. + if self.reachable_blocks.contains(location.block) { + self.storage_liveness.seek_before_primary_effect(location); + let locals_with_storage = self.storage_liveness.get(); + if locals_with_storage.contains(*local) { + self.fail( + location, + format!("StorageLive({local:?}) which already has storage here"), + ); + } + } + } + StatementKind::StorageDead(_) + | StatementKind::Intrinsic(_) + | StatementKind::Coverage(_) + | StatementKind::ConstEvalCounter + | StatementKind::PlaceMention(..) + | StatementKind::Nop => {} + } + + self.super_statement(statement, location); + } + + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + match &terminator.kind { + TerminatorKind::Goto { target } => { + self.check_edge(location, *target, EdgeKind::Normal); + } + TerminatorKind::SwitchInt { targets, discr: _ } => { + for (_, target) in targets.iter() { + self.check_edge(location, target, EdgeKind::Normal); + } + self.check_edge(location, targets.otherwise(), EdgeKind::Normal); + + self.value_cache.clear(); + self.value_cache.extend(targets.iter().map(|(value, _)| value)); + let has_duplicates = targets.iter().len() != self.value_cache.len(); + if has_duplicates { + self.fail( + location, + format!( + "duplicated values in `SwitchInt` terminator: {:?}", + terminator.kind, + ), + ); + } + } + TerminatorKind::Drop { target, unwind, .. } => { + self.check_edge(location, *target, EdgeKind::Normal); + self.check_unwind_edge(location, *unwind); + } + TerminatorKind::Call { args, destination, target, unwind, .. } => { + if let Some(target) = target { + self.check_edge(location, *target, EdgeKind::Normal); + } + self.check_unwind_edge(location, *unwind); + + // The call destination place and Operand::Move place used as an argument might be + // passed by a reference to the callee. Consequently they must be non-overlapping. + // Currently this simply checks for duplicate places. + self.place_cache.clear(); + self.place_cache.insert(destination.as_ref()); + let mut has_duplicates = false; + for arg in args { + if let Operand::Move(place) = arg { + has_duplicates |= !self.place_cache.insert(place.as_ref()); + } + } + + if has_duplicates { + self.fail( + location, + format!( + "encountered overlapping memory in `Call` terminator: {:?}", + terminator.kind, + ), + ); + } + } + TerminatorKind::Assert { target, unwind, .. } => { + self.check_edge(location, *target, EdgeKind::Normal); + self.check_unwind_edge(location, *unwind); + } + TerminatorKind::Yield { resume, drop, .. } => { + if self.body.generator.is_none() { + self.fail(location, "`Yield` cannot appear outside generator bodies"); + } + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail(location, "`Yield` should have been replaced by generator lowering"); + } + self.check_edge(location, *resume, EdgeKind::Normal); + if let Some(drop) = drop { + self.check_edge(location, *drop, EdgeKind::Normal); + } + } + TerminatorKind::FalseEdge { real_target, imaginary_target } => { + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail( + location, + "`FalseEdge` should have been removed after drop elaboration", + ); + } + self.check_edge(location, *real_target, EdgeKind::Normal); + self.check_edge(location, *imaginary_target, EdgeKind::Normal); + } + TerminatorKind::FalseUnwind { real_target, unwind } => { + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail( + location, + "`FalseUnwind` should have been removed after drop elaboration", + ); + } + self.check_edge(location, *real_target, EdgeKind::Normal); + self.check_unwind_edge(location, *unwind); + } + TerminatorKind::InlineAsm { destination, unwind, .. } => { + if let Some(destination) = destination { + self.check_edge(location, *destination, EdgeKind::Normal); + } + self.check_unwind_edge(location, *unwind); + } + TerminatorKind::GeneratorDrop => { + if self.body.generator.is_none() { + self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies"); + } + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + self.fail( + location, + "`GeneratorDrop` should have been replaced by generator lowering", + ); + } + } + TerminatorKind::Resume | TerminatorKind::Terminate => { + let bb = location.block; + if !self.body.basic_blocks[bb].is_cleanup { + self.fail( + location, + "Cannot `Resume` or `Terminate` from non-cleanup basic block", + ) + } + } + TerminatorKind::Return => { + let bb = location.block; + if self.body.basic_blocks[bb].is_cleanup { + self.fail(location, "Cannot `Return` from cleanup basic block") + } + } + TerminatorKind::Unreachable => {} + } + + self.super_terminator(terminator, location); + } + + fn visit_source_scope(&mut self, scope: SourceScope) { + if self.body.source_scopes.get(scope).is_none() { + self.tcx.sess.diagnostic().delay_span_bug( + self.body.span, + format!( + "broken MIR in {:?} ({}):\ninvalid source scope {:?}", + self.body.source.instance, self.when, scope, + ), + ); + } + } +} + +pub fn validate_types<'tcx>( + tcx: TyCtxt<'tcx>, + mir_phase: MirPhase, + param_env: ty::ParamEnv<'tcx>, + body: &Body<'tcx>, +) -> Vec<(Location, String)> { + let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() }; + type_checker.visit_body(body); + type_checker.failures +} + +struct TypeChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + mir_phase: MirPhase, + failures: Vec<(Location, String)>, +} + +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn fail(&mut self, location: Location, msg: impl Into<String>) { + self.failures.push((location, msg.into())); + } + + /// Check if src can be assigned into dest. + /// This is not precise, it will accept some incorrect assignments. + fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool { + // Fast path before we normalize. + if src == dest { + // Equal types, all is good. + return true; + } + + // We sometimes have to use `defining_opaque_types` for subtyping + // to succeed here and figuring out how exactly that should work + // is annoying. It is harmless enough to just not validate anything + // in that case. We still check this after analysis as all opaque + // types have been revealed at this point. + if (src, dest).has_opaque_types() { + return true; + } + + crate::util::is_subtype(self.tcx, self.param_env, src, dest) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed. if self.tcx.sess.opts.unstable_opts.validate_mir @@ -306,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let ty = place.ty(&self.body.local_decls, self.tcx).ty; if !ty.is_copy_modulo_regions(self.tcx, self.param_env) { - self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty)); + self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}")); } } } @@ -325,7 +574,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ProjectionElem::Index(index) => { let index_ty = self.body.local_decls[index].ty; if index_ty != self.tcx.types.usize { - self.fail(location, format!("bad index ({:?} != usize)", index_ty)) + self.fail(location, format!("bad index ({index_ty:?} != usize)")) } } ProjectionElem::Deref @@ -336,22 +585,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if base_ty.is_box() { self.fail( location, - format!("{:?} dereferenced after ElaborateBoxDerefs", base_ty), + format!("{base_ty:?} dereferenced after ElaborateBoxDerefs"), ) } } ProjectionElem::Field(f, ty) => { let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx); - let fail_out_of_bounds = |this: &Self, location| { - this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty)); + let fail_out_of_bounds = |this: &mut Self, location| { + this.fail(location, format!("Out of bounds field {f:?} for {parent_ty:?}")); }; - let check_equal = |this: &Self, location, f_ty| { + let check_equal = |this: &mut Self, location, f_ty| { if !this.mir_assign_valid_types(ty, f_ty) { this.fail( location, format!( - "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`", - place_ref, f, ty, f_ty + "Field projection `{place_ref:?}.{f:?}` specified type `{ty:?}`, but actual type is `{f_ty:?}`" ) ) } @@ -399,7 +647,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let Some(layout) = gen_body.generator_layout() else { self.fail( location, - format!("No generator layout for {:?}", parent_ty), + format!("No generator layout for {parent_ty:?}"), ); return; }; @@ -412,7 +660,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { let Some(f_ty) = layout.field_tys.get(local) else { self.fail( location, - format!("Out of bounds local {:?} for {:?}", local, parent_ty), + format!("Out of bounds local {local:?} for {parent_ty:?}"), ); return; }; @@ -440,9 +688,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) { - let check_place = |place: Place<'_>| { + let check_place = |this: &mut Self, place: Place<'_>| { if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) { - self.fail( + this.fail( START_BLOCK.start_location(), format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name), ); @@ -451,21 +699,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { match debuginfo.value { VarDebugInfoContents::Const(_) => {} VarDebugInfoContents::Place(place) => { - check_place(place); + check_place(self, place); if debuginfo.references != 0 && place.projection.last() == Some(&PlaceElem::Deref) { self.fail( START_BLOCK.start_location(), - format!("debuginfo {:?}, has both ref and deref", debuginfo), + format!("debuginfo {debuginfo:?}, has both ref and deref"), ); } } VarDebugInfoContents::Composite { ty, ref fragments } => { for f in fragments { - check_place(f.contents); + check_place(self, f.contents); if ty.is_union() || ty.is_enum() { self.fail( START_BLOCK.start_location(), - format!("invalid type {:?} for composite debuginfo", ty), + format!("invalid type {ty:?} for composite debuginfo"), ); } if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) { @@ -492,7 +740,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo) && place.projection[1..].contains(&ProjectionElem::Deref) { - self.fail(location, format!("{:?}, has deref at the wrong place", place)); + self.fail(location, format!("{place:?}, has deref at the wrong place")); } self.super_place(place, cntxt, location); @@ -552,7 +800,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Offset => { check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..)); if b != self.tcx.types.isize && b != self.tcx.types.usize { - self.fail(location, format!("Cannot offset by non-isize type {:?}", b)); + self.fail(location, format!("Cannot offset by non-isize type {b:?}")); } } Eq | Lt | Le | Ne | Ge | Gt => { @@ -617,13 +865,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail( location, format!( - "Cannot perform checked arithmetic on unequal types {:?} and {:?}", - a, b + "Cannot perform checked arithmetic on unequal types {a:?} and {b:?}" ), ); } } - _ => self.fail(location, format!("There is no checked version of {:?}", op)), + _ => self.fail(location, format!("There is no checked version of {op:?}")), } } Rvalue::UnaryOp(op, operand) => { @@ -718,7 +965,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::NullaryOp(NullOp::OffsetOf(fields), container) => { - let fail_out_of_bounds = |this: &Self, location, field, ty| { + let fail_out_of_bounds = |this: &mut Self, location, field, ty| { this.fail(location, format!("Out of bounds field {field:?} for {ty:?}")); }; @@ -828,7 +1075,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if !ty.is_bool() { self.fail( location, - format!("`assume` argument must be `bool`, but got: `{}`", ty), + format!("`assume` argument must be `bool`, but got: `{ty}`"), ); } } @@ -841,7 +1088,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } else { self.fail( location, - format!("Expected src to be ptr in copy_nonoverlapping, got: {}", src_ty), + format!("Expected src to be ptr in copy_nonoverlapping, got: {src_ty}"), ); return; }; @@ -851,19 +1098,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } else { self.fail( location, - format!("Expected dst to be ptr in copy_nonoverlapping, got: {}", dst_ty), + format!("Expected dst to be ptr in copy_nonoverlapping, got: {dst_ty}"), ); return; }; // since CopyNonOverlapping is parametrized by 1 type, // we only need to check that they are equal and not keep an extra parameter. if !self.mir_assign_valid_types(op_src_ty, op_dst_ty) { - self.fail(location, format!("bad arg ({:?} != {:?})", op_src_ty, op_dst_ty)); + self.fail(location, format!("bad arg ({op_src_ty:?} != {op_dst_ty:?})")); } let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx); if op_cnt_ty != self.tcx.types.usize { - self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) + self.fail(location, format!("bad arg ({op_cnt_ty:?} != usize)")) } } StatementKind::SetDiscriminant { place, .. } => { @@ -875,8 +1122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail( location, format!( - "`SetDiscriminant` is only allowed on ADTs and generators, not {:?}", - pty + "`SetDiscriminant` is only allowed on ADTs and generators, not {pty:?}" ), ); } @@ -891,29 +1137,11 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) { - self.fail(location, format!("explicit `{:?}` is forbidden", kind)); + self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } - StatementKind::StorageLive(local) => { - // We check that the local is not live when entering a `StorageLive` for it. - // Technically, violating this restriction is only UB and not actually indicative - // of not well-formed MIR. This means that an optimization which turns MIR that - // already has UB into MIR that fails this check is not necessarily wrong. However, - // we have no such optimizations at the moment, and so we include this check anyway - // to help us catch bugs. If you happen to write an optimization that might cause - // this to incorrectly fire, feel free to remove this check. - if self.reachable_blocks.contains(location.block) { - self.storage_liveness.seek_before_primary_effect(location); - let locals_with_storage = self.storage_liveness.get(); - if locals_with_storage.contains(*local) { - self.fail( - location, - format!("StorageLive({local:?}) which already has storage here"), - ); - } - } - } - StatementKind::StorageDead(_) + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) | StatementKind::Coverage(_) | StatementKind::ConstEvalCounter | StatementKind::PlaceMention(..) @@ -925,9 +1153,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { match &terminator.kind { - TerminatorKind::Goto { target } => { - self.check_edge(location, *target, EdgeKind::Normal); - } TerminatorKind::SwitchInt { targets, discr } => { let switch_ty = discr.ty(&self.body.local_decls, self.tcx); @@ -941,164 +1166,49 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { other => bug!("unhandled type: {:?}", other), }); - for (value, target) in targets.iter() { + for (value, _) in targets.iter() { if Scalar::<()>::try_from_uint(value, size).is_none() { self.fail( location, - format!("the value {:#x} is not a proper {:?}", value, switch_ty), + format!("the value {value:#x} is not a proper {switch_ty:?}"), ) } - - self.check_edge(location, target, EdgeKind::Normal); - } - self.check_edge(location, targets.otherwise(), EdgeKind::Normal); - - self.value_cache.clear(); - self.value_cache.extend(targets.iter().map(|(value, _)| value)); - let has_duplicates = targets.iter().len() != self.value_cache.len(); - if has_duplicates { - self.fail( - location, - format!( - "duplicated values in `SwitchInt` terminator: {:?}", - terminator.kind, - ), - ); } } - TerminatorKind::Drop { target, unwind, .. } => { - self.check_edge(location, *target, EdgeKind::Normal); - self.check_unwind_edge(location, *unwind); - } - TerminatorKind::Call { func, args, destination, target, unwind, .. } => { + TerminatorKind::Call { func, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( location, - format!("encountered non-callable type {} in `Call` terminator", func_ty), + format!("encountered non-callable type {func_ty} in `Call` terminator"), ), } - if let Some(target) = target { - self.check_edge(location, *target, EdgeKind::Normal); - } - self.check_unwind_edge(location, *unwind); - - // The call destination place and Operand::Move place used as an argument might be - // passed by a reference to the callee. Consequently they must be non-overlapping. - // Currently this simply checks for duplicate places. - self.place_cache.clear(); - self.place_cache.insert(destination.as_ref()); - let mut has_duplicates = false; - for arg in args { - if let Operand::Move(place) = arg { - has_duplicates |= !self.place_cache.insert(place.as_ref()); - } - } - - if has_duplicates { - self.fail( - location, - format!( - "encountered overlapping memory in `Call` terminator: {:?}", - terminator.kind, - ), - ); - } } - TerminatorKind::Assert { cond, target, unwind, .. } => { + TerminatorKind::Assert { cond, .. } => { let cond_ty = cond.ty(&self.body.local_decls, self.tcx); if cond_ty != self.tcx.types.bool { self.fail( location, format!( - "encountered non-boolean condition of type {} in `Assert` terminator", - cond_ty + "encountered non-boolean condition of type {cond_ty} in `Assert` terminator" ), ); } - self.check_edge(location, *target, EdgeKind::Normal); - self.check_unwind_edge(location, *unwind); - } - TerminatorKind::Yield { resume, drop, .. } => { - if self.body.generator.is_none() { - self.fail(location, "`Yield` cannot appear outside generator bodies"); - } - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { - self.fail(location, "`Yield` should have been replaced by generator lowering"); - } - self.check_edge(location, *resume, EdgeKind::Normal); - if let Some(drop) = drop { - self.check_edge(location, *drop, EdgeKind::Normal); - } - } - TerminatorKind::FalseEdge { real_target, imaginary_target } => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { - self.fail( - location, - "`FalseEdge` should have been removed after drop elaboration", - ); - } - self.check_edge(location, *real_target, EdgeKind::Normal); - self.check_edge(location, *imaginary_target, EdgeKind::Normal); - } - TerminatorKind::FalseUnwind { real_target, unwind } => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { - self.fail( - location, - "`FalseUnwind` should have been removed after drop elaboration", - ); - } - self.check_edge(location, *real_target, EdgeKind::Normal); - self.check_unwind_edge(location, *unwind); - } - TerminatorKind::InlineAsm { destination, unwind, .. } => { - if let Some(destination) = destination { - self.check_edge(location, *destination, EdgeKind::Normal); - } - self.check_unwind_edge(location, *unwind); } - TerminatorKind::GeneratorDrop => { - if self.body.generator.is_none() { - self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies"); - } - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { - self.fail( - location, - "`GeneratorDrop` should have been replaced by generator lowering", - ); - } - } - TerminatorKind::Resume | TerminatorKind::Terminate => { - let bb = location.block; - if !self.body.basic_blocks[bb].is_cleanup { - self.fail( - location, - "Cannot `Resume` or `Terminate` from non-cleanup basic block", - ) - } - } - TerminatorKind::Return => { - let bb = location.block; - if self.body.basic_blocks[bb].is_cleanup { - self.fail(location, "Cannot `Return` from cleanup basic block") - } - } - TerminatorKind::Unreachable => {} + TerminatorKind::Goto { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::Resume + | TerminatorKind::Terminate + | TerminatorKind::Return + | TerminatorKind::Unreachable => {} } self.super_terminator(terminator, location); } - - fn visit_source_scope(&mut self, scope: SourceScope) { - if self.body.source_scopes.get(scope).is_none() { - self.tcx.sess.diagnostic().delay_span_bug( - self.body.span, - format!( - "broken MIR in {:?} ({}):\ninvalid source scope {:?}", - self.body.source.instance, self.when, scope, - ), - ); - } - } } diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs index d6a2ffb7511..83376c8e992 100644 --- a/compiler/rustc_const_eval/src/util/compare_types.rs +++ b/compiler/rustc_const_eval/src/util/compare_types.rs @@ -56,8 +56,16 @@ pub fn is_subtype<'tcx>( // With `Reveal::All`, opaque types get normalized away, with `Reveal::UserFacing` // we would get unification errors because we're unable to look into opaque types, // even if they're constrained in our current function. - // - // It seems very unlikely that this hides any bugs. - let _ = infcx.take_opaque_types(); + for (key, ty) in infcx.take_opaque_types() { + let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); + if hidden_ty != ty.hidden_type.ty { + span_bug!( + ty.hidden_type.span, + "{}, {}", + tcx.type_of(key.def_id).instantiate(tcx, key.args), + ty.hidden_type.ty + ); + } + } errors.is_empty() } diff --git a/compiler/rustc_data_structures/src/base_n.rs b/compiler/rustc_data_structures/src/base_n.rs index 4567759c004..58704350706 100644 --- a/compiler/rustc_data_structures/src/base_n.rs +++ b/compiler/rustc_data_structures/src/base_n.rs @@ -16,22 +16,21 @@ const BASE_64: &[u8; MAX_BASE] = pub fn push_str(mut n: u128, base: usize, output: &mut String) { debug_assert!(base >= 2 && base <= MAX_BASE); let mut s = [0u8; 128]; - let mut index = 0; + let mut index = s.len(); let base = base as u128; loop { + index -= 1; s[index] = BASE_64[(n % base) as usize]; - index += 1; n /= base; if n == 0 { break; } } - s[0..index].reverse(); - output.push_str(str::from_utf8(&s[0..index]).unwrap()); + output.push_str(str::from_utf8(&s[index..]).unwrap()); } #[inline] diff --git a/compiler/rustc_data_structures/src/binary_search_util/mod.rs b/compiler/rustc_data_structures/src/binary_search_util/mod.rs index d40172a2e2f..bc8a6b9eac0 100644 --- a/compiler/rustc_data_structures/src/binary_search_util/mod.rs +++ b/compiler/rustc_data_structures/src/binary_search_util/mod.rs @@ -10,41 +10,17 @@ pub fn binary_search_slice<'d, E, K>(data: &'d [E], key_fn: impl Fn(&E) -> K, ke where K: Ord, { - let Ok(mid) = data.binary_search_by_key(key, &key_fn) else { + let size = data.len(); + let start = data.partition_point(|x| key_fn(x) < *key); + // At this point `start` either points at the first entry with equal or + // greater key or is equal to `size` in case all elements have smaller keys + if start == size || key_fn(&data[start]) != *key { return &[]; }; - let size = data.len(); - - // We get back *some* element with the given key -- so do - // a galloping search backwards to find the *first* one. - let mut start = mid; - let mut previous = mid; - let mut step = 1; - loop { - start = start.saturating_sub(step); - if start == 0 || key_fn(&data[start]) != *key { - break; - } - previous = start; - step *= 2; - } - step = previous - start; - while step > 1 { - let half = step / 2; - let mid = start + half; - if key_fn(&data[mid]) != *key { - start = mid; - } - step -= half; - } - // adjust by one if we have overshot - if start < size && key_fn(&data[start]) != *key { - start += 1; - } // Now search forward to find the *last* one. - let mut end = mid; - let mut previous = mid; + let mut end = start; + let mut previous = start; let mut step = 1; loop { end = end.saturating_add(step).min(size); diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 9409057d484..60b343afbed 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -49,12 +49,11 @@ impl<K: Ord, V> SortedMap<K, V> { } #[inline] - pub fn insert(&mut self, key: K, mut value: V) -> Option<V> { + pub fn insert(&mut self, key: K, value: V) -> Option<V> { match self.lookup_index_for(&key) { Ok(index) => { let slot = unsafe { self.data.get_unchecked_mut(index) }; - mem::swap(&mut slot.1, &mut value); - Some(value) + Some(mem::replace(&mut slot.1, value)) } Err(index) => { self.data.insert(index, (key, value)); diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index 99581ed2375..04e359a5470 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -268,11 +268,7 @@ impl<K: Eq + Hash, V> SsoHashMap<K, V> { pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> { match self { SsoHashMap::Array(array) => { - if let Some(index) = array.iter().position(|(k, _v)| k == key) { - Some(array.swap_remove(index)) - } else { - None - } + array.iter().position(|(k, _v)| k == key).map(|index| array.swap_remove(index)) } SsoHashMap::Map(map) => map.remove_entry(key), } diff --git a/compiler/rustc_data_structures/src/sync/vec.rs b/compiler/rustc_data_structures/src/sync/vec.rs index e36dded9e5e..314496ce9f0 100644 --- a/compiler/rustc_data_structures/src/sync/vec.rs +++ b/compiler/rustc_data_structures/src/sync/vec.rs @@ -43,37 +43,23 @@ impl<I: Idx, T: Copy> AppendOnlyIndexVec<I, T> { #[derive(Default)] pub struct AppendOnlyVec<T: Copy> { - #[cfg(not(parallel_compiler))] - vec: elsa::vec::FrozenVec<T>, - #[cfg(parallel_compiler)] - vec: elsa::sync::LockFreeFrozenVec<T>, + vec: parking_lot::RwLock<Vec<T>>, } impl<T: Copy> AppendOnlyVec<T> { pub fn new() -> Self { - Self { - #[cfg(not(parallel_compiler))] - vec: elsa::vec::FrozenVec::new(), - #[cfg(parallel_compiler)] - vec: elsa::sync::LockFreeFrozenVec::new(), - } + Self { vec: Default::default() } } pub fn push(&self, val: T) -> usize { - #[cfg(not(parallel_compiler))] - let i = self.vec.len(); - #[cfg(not(parallel_compiler))] - self.vec.push(val); - #[cfg(parallel_compiler)] - let i = self.vec.push(val); - i + let mut v = self.vec.write(); + let n = v.len(); + v.push(val); + n } pub fn get(&self, i: usize) -> Option<T> { - #[cfg(not(parallel_compiler))] - return self.vec.get_copy(i); - #[cfg(parallel_compiler)] - return self.vec.get(i); + self.vec.read().get(i).copied() } pub fn iter_enumerated(&self) -> impl Iterator<Item = (usize, T)> + '_ { diff --git a/compiler/rustc_data_structures/src/sync/worker_local.rs b/compiler/rustc_data_structures/src/sync/worker_local.rs index d61bb55be68..8c84daf4f16 100644 --- a/compiler/rustc_data_structures/src/sync/worker_local.rs +++ b/compiler/rustc_data_structures/src/sync/worker_local.rs @@ -116,7 +116,7 @@ pub struct WorkerLocal<T> { // This is safe because the `deref` call will return a reference to a `T` unique to each thread // or it will panic for threads without an associated local. So there isn't a need for `T` to do -// it's own synchronization. The `verify` method on `RegistryId` has an issue where the the id +// it's own synchronization. The `verify` method on `RegistryId` has an issue where the id // can be reused, but `WorkerLocal` has a reference to `Registry` which will prevent any reuse. #[cfg(parallel_compiler)] unsafe impl<T: Send> Sync for WorkerLocal<T> {} diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 67352c55c90..a7b01618ade 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [lib] [dependencies] +time = { version = "0.3", default-features = false, features = ["formatting", ] } tracing = { version = "0.1.35" } serde_json = "1.0.59" rustc_log = { path = "../rustc_log" } diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 22b4ec6b0d1..d3bd3244a52 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -3,7 +3,11 @@ driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden driver_impl_ice_flags = compiler flags: {$flags} +driver_impl_ice_path = please attach the file at `{$path}` to your bug report +driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} +driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` driver_impl_ice_version = rustc {$version} running on {$triple} + driver_impl_rlink_empty_version_number = The input does not contain version number driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` @@ -15,5 +19,3 @@ driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc ver driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file - -driver_impl_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}` diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 25c043149e8..8f8b9eaa274 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -7,6 +7,8 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(lazy_cell)] #![feature(decl_macro)] +#![feature(ice_to_disk)] +#![feature(let_chains)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] @@ -35,9 +37,7 @@ use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; -use rustc_session::config::{ - ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths, -}; +use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType, TrimmedDefPaths}; use rustc_session::cstore::MetadataLoader; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -51,14 +51,18 @@ use std::cmp::max; use std::collections::BTreeMap; use std::env; use std::ffi::OsString; +use std::fmt::Write as _; use std::fs; use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, catch_unwind}; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::OnceLock; -use std::time::Instant; +use std::time::{Instant, SystemTime}; +use time::format_description::well_known::Rfc3339; +use time::OffsetDateTime; #[allow(unused_macros)] macro do_not_use_print($($t:tt)*) { @@ -67,6 +71,11 @@ macro do_not_use_print($($t:tt)*) { ) } +#[allow(unused_macros)] +macro do_not_use_safe_print($($t:tt)*) { + std::compile_error!("Don't use `safe_print` or `safe_println` here, use `println_info` instead") +} + // This import blocks the use of panicking `print` and `println` in all the code // below. Please use `safe_print` and `safe_println` to avoid ICE when // encountering an I/O error during print. @@ -294,6 +303,7 @@ fn run_compiler( input: Input::File(PathBuf::new()), output_file: ofile, output_dir: odir, + ice_file: ice_path().clone(), file_loader, locale_resources: DEFAULT_LOCALE_RESOURCES, lint_caps: Default::default(), @@ -714,10 +724,17 @@ fn print_crate_info( sess: &Session, parse_attrs: bool, ) -> Compilation { - use rustc_session::config::PrintRequest::*; + use rustc_session::config::PrintKind::*; + + // This import prevents the following code from using the printing macros + // used by the rest of the module. Within this function, we only write to + // the output specified by `sess.io.output_file`. + #[allow(unused_imports)] + use {do_not_use_safe_print as safe_print, do_not_use_safe_print as safe_println}; + // NativeStaticLibs and LinkArgs are special - printed during linking // (empty iterator returns true) - if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) { + if sess.opts.prints.iter().all(|p| p.kind == NativeStaticLibs || p.kind == LinkArgs) { return Compilation::Continue; } @@ -733,17 +750,23 @@ fn print_crate_info( } else { None }; + for req in &sess.opts.prints { - match *req { + let mut crate_info = String::new(); + macro println_info($($arg:tt)*) { + crate_info.write_fmt(format_args!("{}\n", format_args!($($arg)*))).unwrap() + } + + match req.kind { TargetList => { let mut targets = rustc_target::spec::TARGETS.to_vec(); targets.sort_unstable(); - safe_println!("{}", targets.join("\n")); + println_info!("{}", targets.join("\n")); } - Sysroot => safe_println!("{}", sess.sysroot.display()), - TargetLibdir => safe_println!("{}", sess.target_tlib_path.dir.display()), + Sysroot => println_info!("{}", sess.sysroot.display()), + TargetLibdir => println_info!("{}", sess.target_tlib_path.dir.display()), TargetSpec => { - safe_println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); + println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } AllTargetSpecs => { let mut targets = BTreeMap::new(); @@ -752,26 +775,30 @@ fn print_crate_info( let target = Target::expect_builtin(&triple); targets.insert(name, target.to_json()); } - safe_println!("{}", serde_json::to_string_pretty(&targets).unwrap()); + println_info!("{}", serde_json::to_string_pretty(&targets).unwrap()); } - FileNames | CrateName => { + FileNames => { let Some(attrs) = attrs.as_ref() else { // no crate attributes, print out an error and exit return Compilation::Continue; }; let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess); let id = rustc_session::output::find_crate_name(sess, attrs); - if *req == PrintRequest::CrateName { - safe_println!("{id}"); - continue; - } let crate_types = collect_crate_types(sess, attrs); for &style in &crate_types { let fname = rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); + println_info!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); } } + CrateName => { + let Some(attrs) = attrs.as_ref() else { + // no crate attributes, print out an error and exit + return Compilation::Continue; + }; + let id = rustc_session::output::find_crate_name(sess, attrs); + println_info!("{id}"); + } Cfg => { let mut cfgs = sess .parse_sess @@ -803,13 +830,13 @@ fn print_crate_info( cfgs.sort(); for cfg in cfgs { - safe_println!("{cfg}"); + println_info!("{cfg}"); } } CallingConventions => { let mut calling_conventions = rustc_target::spec::abi::all_names(); calling_conventions.sort_unstable(); - safe_println!("{}", calling_conventions.join("\n")); + println_info!("{}", calling_conventions.join("\n")); } RelocationModels | CodeModels @@ -817,7 +844,7 @@ fn print_crate_info( | TargetCPUs | StackProtectorStrategies | TargetFeatures => { - codegen_backend.print(*req, sess); + codegen_backend.print(req, &mut crate_info, sess); } // Any output here interferes with Cargo's parsing of other printed output NativeStaticLibs => {} @@ -827,7 +854,7 @@ fn print_crate_info( for split in &[Off, Packed, Unpacked] { if sess.target.options.supported_split_debuginfo.contains(split) { - safe_println!("{split}"); + println_info!("{split}"); } } } @@ -835,7 +862,7 @@ fn print_crate_info( use rustc_target::spec::current_apple_deployment_target; if sess.target.is_like_osx { - safe_println!( + println_info!( "deployment_target={}", current_apple_deployment_target(&sess.target) .expect("unknown Apple target OS") @@ -846,6 +873,8 @@ fn print_crate_info( } } } + + req.out.overwrite(&crate_info, sess); } Compilation::Stop } @@ -1292,9 +1321,29 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { } } -/// Stores the default panic hook, from before [`install_ice_hook`] was called. -static DEFAULT_HOOK: OnceLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = - OnceLock::new(); +pub static ICE_PATH: OnceLock<Option<PathBuf>> = OnceLock::new(); + +pub fn ice_path() -> &'static Option<PathBuf> { + ICE_PATH.get_or_init(|| { + if !rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build() { + return None; + } + if let Ok("0") = std::env::var("RUST_BACKTRACE").as_deref() { + return None; + } + let mut path = match std::env::var("RUSTC_ICE").as_deref() { + // Explicitly opting out of writing ICEs to disk. + Ok("0") => return None, + Ok(s) => PathBuf::from(s), + Err(_) => std::env::current_dir().unwrap_or_default(), + }; + let now: OffsetDateTime = SystemTime::now().into(); + let file_now = now.format(&Rfc3339).unwrap_or(String::new()); + let pid = std::process::id(); + path.push(format!("rustc-ice-{file_now}-{pid}.txt")); + Some(path) + }) +} /// Installs a panic hook that will print the ICE message on unexpected panics. /// @@ -1318,8 +1367,6 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) std::env::set_var("RUST_BACKTRACE", "full"); } - let default_hook = DEFAULT_HOOK.get_or_init(panic::take_hook); - panic::set_hook(Box::new(move |info| { // If the error was caused by a broken pipe then this is not a bug. // Write the error and return immediately. See #98700. @@ -1336,7 +1383,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler)) // Invoke the default handler, which prints the actual panic message and optionally a backtrace // Don't do this for delayed bugs, which already emit their own more useful backtrace. if !info.payload().is::<rustc_errors::DelayedBugPanic>() { - (*default_hook)(info); + std::panic_hook_with_disk_dump(info, ice_path().as_deref()); // Separate the output with an empty line eprintln!(); @@ -1368,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); + let handler = rustc_errors::Handler::with_emitter(emitter); // a .span_bug or .bug call has already printed what // it wants to print. @@ -1379,10 +1426,40 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: } handler.emit_note(session_diagnostics::IceBugReport { bug_report_url }); - handler.emit_note(session_diagnostics::IceVersion { - version: util::version_str!().unwrap_or("unknown_version"), - triple: config::host_triple(), - }); + + let version = util::version_str!().unwrap_or("unknown_version"); + let triple = config::host_triple(); + + static FIRST_PANIC: AtomicBool = AtomicBool::new(true); + + let file = if let Some(path) = ice_path().as_ref() { + // Create the ICE dump target file. + match crate::fs::File::options().create(true).append(true).open(&path) { + Ok(mut file) => { + handler + .emit_note(session_diagnostics::IcePath { path: path.display().to_string() }); + if FIRST_PANIC.swap(false, Ordering::SeqCst) { + let _ = write!(file, "\n\nrustc version: {version}\nplatform: {triple}"); + } + Some(file) + } + Err(err) => { + // The path ICE couldn't be written to disk, provide feedback to the user as to why. + handler.emit_warning(session_diagnostics::IcePathError { + path: path.display().to_string(), + error: err.to_string(), + env_var: std::env::var("RUSTC_ICE") + .ok() + .map(|env_var| session_diagnostics::IcePathErrorEnv { env_var }), + }); + handler.emit_note(session_diagnostics::IceVersion { version, triple }); + None + } + } + } else { + handler.emit_note(session_diagnostics::IceVersion { version, triple }); + None + }; if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() { handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") }); @@ -1396,7 +1473,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: let num_frames = if backtrace { None } else { Some(2) }; - interface::try_print_query_stack(&handler, num_frames); + interface::try_print_query_stack(&handler, num_frames, file); // We don't trust this callback not to panic itself, so run it at the end after we're sure we've // printed all the relevant info. diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 24a5f4030b8..222c7b5d6a7 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -1,6 +1,5 @@ //! The various pretty-printing routines. -use crate::session_diagnostics::UnprettyDumpFail; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_errors::ErrorGuaranteed; @@ -358,17 +357,7 @@ fn get_source(sess: &Session) -> (String, FileName) { } fn write_or_print(out: &str, sess: &Session) { - match &sess.io.output_file { - None | Some(OutFileName::Stdout) => print!("{out}"), - Some(OutFileName::Real(p)) => { - if let Err(e) = std::fs::write(p, out) { - sess.emit_fatal(UnprettyDumpFail { - path: p.display().to_string(), - err: e.to_string(), - }); - } - } - } + sess.io.output_file.as_ref().unwrap_or(&OutFileName::Stdout).overwrite(out, sess); } pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) { diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 638b368f702..5eb587c54d9 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -1,4 +1,4 @@ -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, Subdiagnostic}; #[derive(Diagnostic)] #[diag(driver_impl_rlink_unable_to_read)] @@ -33,13 +33,6 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> { pub(crate) struct RlinkNotAFile; #[derive(Diagnostic)] -#[diag(driver_impl_unpretty_dump_fail)] -pub(crate) struct UnprettyDumpFail { - pub path: String, - pub err: String, -} - -#[derive(Diagnostic)] #[diag(driver_impl_ice)] pub(crate) struct Ice; @@ -57,6 +50,27 @@ pub(crate) struct IceVersion<'a> { } #[derive(Diagnostic)] +#[diag(driver_impl_ice_path)] +pub(crate) struct IcePath { + pub path: String, +} + +#[derive(Diagnostic)] +#[diag(driver_impl_ice_path_error)] +pub(crate) struct IcePathError { + pub path: String, + pub error: String, + #[subdiagnostic] + pub env_var: Option<IcePathErrorEnv>, +} + +#[derive(Subdiagnostic)] +#[note(driver_impl_ice_path_error_env)] +pub(crate) struct IcePathErrorEnv { + pub env_var: String, +} + +#[derive(Diagnostic)] #[diag(driver_impl_ice_flags)] pub(crate) struct IceFlags { pub flags: String, diff --git a/compiler/rustc_error_codes/src/error_codes/E0391.md b/compiler/rustc_error_codes/src/error_codes/E0391.md index dff50ccaa0b..457fbd002a1 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0391.md +++ b/compiler/rustc_error_codes/src/error_codes/E0391.md @@ -14,3 +14,6 @@ trait SecondTrait : FirstTrait { The previous example contains a circular dependency between two traits: `FirstTrait` depends on `SecondTrait` which itself depends on `FirstTrait`. + +See https://rustc-dev-guide.rust-lang.org/overview.html#queries and +https://rustc-dev-guide.rust-lang.org/query.html for more information. diff --git a/compiler/rustc_error_codes/src/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md index 60060cacbd6..483c74c0ff5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0691.md +++ b/compiler/rustc_error_codes/src/error_codes/E0691.md @@ -11,7 +11,8 @@ struct ForceAlign32; #[repr(transparent)] struct Wrapper(f32, ForceAlign32); // error: zero-sized field in transparent - // struct has alignment larger than 1 + // struct has alignment of 32, which + // is larger than 1 ``` A transparent struct, enum, or union is supposed to be represented exactly like diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 51e1fe531dd..77e2c900c0d 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -71,17 +71,17 @@ pub enum TranslationBundleError { impl fmt::Display for TranslationBundleError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TranslationBundleError::ReadFtl(e) => write!(f, "could not read ftl file: {}", e), + TranslationBundleError::ReadFtl(e) => write!(f, "could not read ftl file: {e}"), TranslationBundleError::ParseFtl(e) => { - write!(f, "could not parse ftl file: {}", e) + write!(f, "could not parse ftl file: {e}") } - TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {}", e), + TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {e}"), TranslationBundleError::MissingLocale => write!(f, "missing locale directory"), TranslationBundleError::ReadLocalesDir(e) => { - write!(f, "could not read locales dir: {}", e) + write!(f, "could not read locales dir: {e}") } TranslationBundleError::ReadLocalesDirEntry(e) => { - write!(f, "could not read locales dir entry: {}", e) + write!(f, "could not read locales dir entry: {e}") } TranslationBundleError::LocaleIsNotDir => { write!(f, "`$sysroot/share/locales/$locale` is not a directory") @@ -354,6 +354,13 @@ impl DiagnosticMessage { } } } + + pub fn as_str(&self) -> Option<&str> { + match self { + DiagnosticMessage::Eager(s) | DiagnosticMessage::Str(s) => Some(s), + DiagnosticMessage::FluentIdentifier(_, _) => None, + } + } } impl From<String> for DiagnosticMessage { @@ -526,6 +533,14 @@ impl MultiSpan { pub fn has_span_labels(&self) -> bool { self.span_labels.iter().any(|(sp, _)| !sp.is_dummy()) } + + /// Clone this `MultiSpan` without keeping any of the span labels - sometimes a `MultiSpan` is + /// to be re-used in another diagnostic, but includes `span_labels` which have translated + /// messages. These translated messages would fail to translate without their diagnostic + /// arguments which are unlikely to be cloned alongside the `Span`. + pub fn clone_ignoring_labels(&self) -> Self { + Self { primary_spans: self.primary_spans.clone(), ..MultiSpan::new() } + } } impl From<Span> for MultiSpan { diff --git a/compiler/rustc_errors/messages.ftl b/compiler/rustc_errors/messages.ftl index 8e8223c3cf8..d68dba0be5e 100644 --- a/compiler/rustc_errors/messages.ftl +++ b/compiler/rustc_errors/messages.ftl @@ -1,3 +1,25 @@ +errors_delayed_at_with_newline = + delayed at {$emitted_at} + {$note} + +errors_delayed_at_without_newline = + delayed at {$emitted_at} - {$note} + +errors_expected_lifetime_parameter = + expected lifetime {$count -> + [1] parameter + *[other] parameters + } + +errors_indicate_anonymous_lifetime = + indicate the anonymous {$count -> + [1] lifetime + *[other] lifetimes + } + +errors_invalid_flushed_delayed_diagnostic_level = + `flushed_delayed` got diagnostic with level {$level}, instead of the expected `DelayedBug` + errors_target_inconsistent_architecture = inconsistent target specification: "data-layout" claims architecture is {$dl}-endian, while "target-endian" is `{$target}` diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 9872b3bda1e..a88fba6dae6 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -157,10 +157,8 @@ impl AnnotateSnippetEmitterWriter { { annotated_files.swap(0, pos); } - // owned: line source, line index, annotations - type Owned = (String, usize, Vec<crate::snippet::Annotation>); - let filename = source_map.filename_for_diagnostics(&primary_lo.file.name); - let origin = filename.to_string_lossy(); + // owned: file name, line source, line index, annotations + type Owned = (String, String, usize, Vec<crate::snippet::Annotation>); let annotated_files: Vec<Owned> = annotated_files .into_iter() .flat_map(|annotated_file| { @@ -169,7 +167,15 @@ impl AnnotateSnippetEmitterWriter { .lines .into_iter() .map(|line| { - (source_string(file.clone(), &line), line.line_index, line.annotations) + // Ensure the source file is present before we try + // to load a string from it. + source_map.ensure_source_file_source_present(file.clone()); + ( + format!("{}", source_map.filename_for_diagnostics(&file.name)), + source_string(file.clone(), &line), + line.line_index, + line.annotations, + ) }) .collect::<Vec<Owned>>() }) @@ -192,11 +198,11 @@ impl AnnotateSnippetEmitterWriter { }, slices: annotated_files .iter() - .map(|(source, line_index, annotations)| { + .map(|(file_name, source, line_index, annotations)| { Slice { source, line_start: *line_index, - origin: Some(&origin), + origin: Some(&file_name), // FIXME(#59346): Not really sure when `fold` should be true or false fold: false, annotations: annotations diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index ed0d06ed0ff..a96e317df55 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -420,13 +420,13 @@ impl Diagnostic { let expected_label = if expected_label.is_empty() { "expected".to_string() } else { - format!("expected {}", expected_label) + format!("expected {expected_label}") }; let found_label = found_label.to_string(); let found_label = if found_label.is_empty() { "found".to_string() } else { - format!("found {}", found_label) + format!("found {found_label}") }; let (found_padding, expected_padding) = if expected_label.len() > found_label.len() { (expected_label.len() - found_label.len(), 0) @@ -439,13 +439,13 @@ impl Diagnostic { StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), })); - msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); + msg.push((format!("`{expected_extra}\n"), Style::NoStyle)); msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle)); msg.extend(found.0.iter().map(|x| match *x { StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle), StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight), })); - msg.push((format!("`{}", found_extra), Style::NoStyle)); + msg.push((format!("`{found_extra}"), Style::NoStyle)); // For now, just attach these as notes. self.highlighted_note(msg); @@ -454,7 +454,7 @@ impl Diagnostic { pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ - (format!("`{}` from trait: `", name), Style::NoStyle), + (format!("`{name}` from trait: `"), Style::NoStyle), (signature, Style::Highlight), ("`".to_string(), Style::NoStyle), ]); diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 08ff2cfba5c..5e23ae655fe 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/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 10fe7fc74a8..a170e3a8943 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -1,3 +1,4 @@ +use crate::diagnostic::DiagnosticLocation; use crate::{fluent_generated as fluent, AddToDiagnostic}; use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg}; use rustc_ast as ast; @@ -10,6 +11,7 @@ use rustc_span::Span; use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use rustc_type_ir as type_ir; +use std::backtrace::Backtrace; use std::borrow::Cow; use std::fmt; use std::num::ParseIntError; @@ -102,7 +104,7 @@ impl IntoDiagnosticArg for bool { impl IntoDiagnosticArg for char { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) + DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}"))) } } @@ -164,6 +166,12 @@ impl IntoDiagnosticArg for hir::ConstContext { } } +impl IntoDiagnosticArg for ast::Expr { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) + } +} + impl IntoDiagnosticArg for ast::Path { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) @@ -311,3 +319,62 @@ pub enum LabelKind { Label, Help, } + +#[derive(Subdiagnostic)] +#[label(errors_expected_lifetime_parameter)] +pub struct ExpectedLifetimeParameter { + #[primary_span] + pub span: Span, + pub count: usize, +} + +#[derive(Subdiagnostic)] +#[note(errors_delayed_at_with_newline)] +pub struct DelayedAtWithNewline { + #[primary_span] + pub span: Span, + pub emitted_at: DiagnosticLocation, + pub note: Backtrace, +} +#[derive(Subdiagnostic)] +#[note(errors_delayed_at_without_newline)] +pub struct DelayedAtWithoutNewline { + #[primary_span] + pub span: Span, + pub emitted_at: DiagnosticLocation, + pub note: Backtrace, +} + +impl IntoDiagnosticArg for DiagnosticLocation { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::from(self.to_string())) + } +} + +impl IntoDiagnosticArg for Backtrace { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::from(self.to_string())) + } +} + +#[derive(Subdiagnostic)] +#[note(errors_invalid_flushed_delayed_diagnostic_level)] +pub struct InvalidFlushedDelayedDiagnosticLevel { + #[primary_span] + pub span: Span, + pub level: rustc_errors::Level, +} +impl IntoDiagnosticArg for rustc_errors::Level { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::from(self.to_string())) + } +} + +#[derive(Subdiagnostic)] +#[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")] +pub struct IndicateAnonymousLifetime { + #[primary_span] + pub span: Span, + pub count: usize, + pub suggestion: String, +} diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 9d4d159fd96..44654571d43 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -279,12 +279,12 @@ pub trait Emitter: Translate { let msg = if substitution.is_empty() || sugg.style.hide_inline() { // This substitution is only removal OR we explicitly don't want to show the // code inline (`hide_inline`). Therefore, we don't show the substitution. - format!("help: {}", &msg) + format!("help: {msg}") } else { // Show the default suggestion text with the substitution format!( "help: {}{}: `{}`", - &msg, + msg, if self.source_map().is_some_and(|sm| is_case_difference( sm, substitution, @@ -1982,7 +1982,7 @@ impl EmitterWriter { // We special case `#[derive(_)]\n` and other attribute suggestions, because those // are the ones where context is most useful. let file_lines = sm - .span_to_lines(span.primary_span().unwrap().shrink_to_hi()) + .span_to_lines(parts[0].span.shrink_to_hi()) .expect("span_to_lines failed when emitting suggestion"); let line_num = sm.lookup_char_pos(parts[0].span.lo()).line; if let Some(line) = file_lines.file.get_line(line_num - 1) { @@ -2145,7 +2145,7 @@ impl EmitterWriter { &mut self.dst, self.short_message, ) { - panic!("failed to emit error: {}", e) + panic!("failed to emit error: {e}") } } if !self.short_message { @@ -2161,7 +2161,7 @@ impl EmitterWriter { true, None, ) { - panic!("failed to emit error: {}", err); + panic!("failed to emit error: {err}"); } } for sugg in suggestions { @@ -2180,7 +2180,7 @@ impl EmitterWriter { true, None, ) { - panic!("failed to emit error: {}", e); + panic!("failed to emit error: {e}"); } } SuggestionStyle::HideCodeInline @@ -2193,22 +2193,22 @@ impl EmitterWriter { &Level::Help, max_line_num_len, ) { - panic!("failed to emit error: {}", e); + panic!("failed to emit error: {e}"); } } } } } } - Err(e) => panic!("failed to emit error: {}", e), + Err(e) => panic!("failed to emit error: {e}"), } let mut dst = self.dst.writable(); match writeln!(dst) { - Err(e) => panic!("failed to emit error: {}", e), + Err(e) => panic!("failed to emit error: {e}"), _ => { if let Err(e) = dst.flush() { - panic!("failed to emit error: {}", e) + panic!("failed to emit error: {e}") } } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index f32d6b96b9b..55f7c485024 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -159,7 +159,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print diagnostics: {:?}", e); + panic!("failed to print diagnostics: {e:?}"); } } @@ -172,7 +172,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print notification: {:?}", e); + panic!("failed to print notification: {e:?}"); } } @@ -194,7 +194,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print future breakage report: {:?}", e); + panic!("failed to print future breakage report: {e:?}"); } } @@ -208,7 +208,7 @@ impl Emitter for JsonEmitter { } .and_then(|_| self.dst.flush()); if let Err(e) = result { - panic!("failed to print unused externs: {:?}", e); + panic!("failed to print unused externs: {e:?}"); } } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 671dc449eaa..1f9a2981e02 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)); + 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 b9db25103a3..3d1639db4af 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -22,6 +22,8 @@ extern crate rustc_macros; #[macro_use] extern crate tracing; +extern crate self as rustc_errors; + pub use emitter::ColorConfig; use rustc_lint_defs::LintExpectationId; @@ -47,9 +49,10 @@ use std::borrow::Cow; use std::error::Report; use std::fmt; use std::hash::Hash; +use std::io::Write; use std::num::NonZeroUsize; use std::panic; -use std::path::Path; +use std::path::{Path, PathBuf}; use termcolor::{Color, ColorSpec}; @@ -376,13 +379,16 @@ pub struct ExplicitBug; /// rather than a failed assertion, etc. pub struct DelayedBugPanic; +use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline}; pub use diagnostic::{ AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, }; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; pub use diagnostic_impls::{ - DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans, + DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, + IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, LabelKind, + SingleLabelManySpans, }; use std::backtrace::{Backtrace, BacktraceStatus}; @@ -390,7 +396,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<HandlerInner>, } @@ -461,6 +466,10 @@ struct HandlerInner { /// /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html fulfilled_expectations: FxHashSet<LintExpectationId>, + + /// The file where the ICE information is stored. This allows delayed_span_bug backtraces to be + /// stored along side the main panic backtrace. + ice_file: Option<PathBuf>, } /// A key denoting where from a diagnostic was stashed. @@ -544,63 +553,47 @@ impl Drop for HandlerInner { impl Handler { pub fn with_tty_emitter( - color_config: ColorConfig, - can_emit_warnings: bool, - treat_err_as_bug: Option<NonZeroUsize>, sm: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, ) -> Self { - Self::with_tty_emitter_and_flags( - color_config, - sm, - fluent_bundle, - fallback_bundle, - HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, - ) - } - - pub fn with_tty_emitter_and_flags( - color_config: ColorConfig, - sm: Option<Lrc<SourceMap>>, - fluent_bundle: Option<Lrc<FluentBundle>>, - fallback_bundle: LazyFallbackBundle, - flags: HandlerFlags, - ) -> 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) + 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<NonZeroUsize>, - emitter: Box<dyn Emitter + sync::Send>, - ) -> Self { - Handler::with_emitter_and_flags( - emitter, - HandlerFlags { can_emit_warnings, treat_err_as_bug, ..Default::default() }, - ) + 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<dyn Emitter + sync::Send>, - flags: HandlerFlags, - ) -> 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<dyn Emitter + sync::Send>) -> 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, @@ -618,6 +611,7 @@ impl Handler { check_unstable_expect_diagnostics: false, unstable_expect_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), + ice_file: None, }), } } @@ -645,7 +639,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. @@ -991,7 +985,7 @@ impl Handler { self.emit_diag_at_span(Diagnostic::new_with_code(Warning(None), Some(code), msg), span); } - pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { + pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { self.inner.borrow_mut().span_bug(span, msg) } @@ -1000,7 +994,7 @@ impl Handler { pub fn delay_span_bug( &self, span: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, + msg: impl Into<String>, ) -> ErrorGuaranteed { self.inner.borrow_mut().delay_span_bug(span, msg) } @@ -1473,7 +1467,7 @@ impl HandlerInner { let _ = self.fatal(errors); } (_, _) => { - let _ = self.fatal(format!("{}; {}", &errors, &warnings)); + let _ = self.fatal(format!("{errors}; {warnings}")); } } @@ -1584,8 +1578,8 @@ impl HandlerInner { } #[track_caller] - fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! { - self.emit_diag_at_span(Diagnostic::new(Bug, msg), sp); + fn span_bug(&mut self, sp: impl Into<MultiSpan>, msg: impl Into<String>) -> ! { + self.emit_diag_at_span(Diagnostic::new(Bug, msg.into()), sp); panic::panic_any(ExplicitBug); } @@ -1598,7 +1592,7 @@ impl HandlerInner { fn delay_span_bug( &mut self, sp: impl Into<MultiSpan>, - msg: impl Into<DiagnosticMessage>, + msg: impl Into<String>, ) -> ErrorGuaranteed { // This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before // incrementing `err_count` by one, so we need to +1 the comparing. @@ -1607,9 +1601,9 @@ impl HandlerInner { self.err_count() + self.lint_err_count + self.delayed_bug_count() + 1 >= c.get() }) { // FIXME: don't abort here if report_delayed_bugs is off - self.span_bug(sp, msg); + self.span_bug(sp, msg.into()); } - let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); + let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg.into()); diagnostic.set_span(sp.into()); self.emit_diagnostic(&mut diagnostic).unwrap() } @@ -1657,8 +1651,21 @@ impl HandlerInner { explanation: impl Into<DiagnosticMessage> + Copy, ) { let mut no_bugs = true; + // If backtraces are enabled, also print the query stack + let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); for bug in bugs { - let mut bug = bug.decorate(); + if let Some(file) = self.ice_file.as_ref() + && let Ok(mut out) = std::fs::File::options().create(true).append(true).open(file) + { + let _ = write!( + &mut out, + "delayed span bug: {}\n{}\n", + bug.inner.styled_message().iter().filter_map(|(msg, _)| msg.as_str()).collect::<String>(), + &bug.note + ); + } + let mut bug = + if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner }; if no_bugs { // Put the overall explanation before the `DelayedBug`s, to @@ -1671,11 +1678,10 @@ impl HandlerInner { if bug.level != Level::DelayedBug { // NOTE(eddyb) not panicking here because we're already producing // an ICE, and the more information the merrier. - bug.note(format!( - "`flushed_delayed` got diagnostic with level {:?}, \ - instead of the expected `DelayedBug`", - bug.level, - )); + bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel { + span: bug.span.primary_span().unwrap(), + level: bug.level, + }); } bug.level = Level::Bug; @@ -1714,13 +1720,11 @@ impl HandlerInner { (count, delayed_count, as_bug) => { if delayed_count > 0 { panic!( - "aborting after {} errors and {} delayed bugs due to `-Z treat-err-as-bug={}`", - count, delayed_count, as_bug, + "aborting after {count} errors and {delayed_count} delayed bugs due to `-Z treat-err-as-bug={as_bug}`", ) } else { panic!( - "aborting after {} errors due to `-Z treat-err-as-bug={}`", - count, as_bug, + "aborting after {count} errors due to `-Z treat-err-as-bug={as_bug}`", ) } } @@ -1742,12 +1746,22 @@ impl DelayedDiagnostic { fn decorate(mut self) -> Diagnostic { match self.note.status() { BacktraceStatus::Captured => { - self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); + let inner = &self.inner; + self.inner.subdiagnostic(DelayedAtWithNewline { + span: inner.span.primary_span().unwrap(), + emitted_at: inner.emitted_at.clone(), + note: self.note, + }); } // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. _ => { - self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note)); + let inner = &self.inner; + self.inner.subdiagnostic(DelayedAtWithoutNewline { + span: inner.span.primary_span().unwrap(), + emitted_at: inner.emitted_at.clone(), + note: self.note, + }); } } @@ -1839,20 +1853,36 @@ pub fn add_elided_lifetime_in_path_suggestion( incl_angl_brckt: bool, insertion_span: Span, ) { - diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n))); + diag.subdiagnostic(ExpectedLifetimeParameter { span: path_span, count: n }); if !source_map.is_span_accessible(insertion_span) { // Do not try to suggest anything if generated by a proc-macro. return; } let anon_lts = vec!["'_"; n].join(", "); let suggestion = - if incl_angl_brckt { format!("<{}>", anon_lts) } else { format!("{}, ", anon_lts) }; - diag.span_suggestion_verbose( - insertion_span.shrink_to_hi(), - format!("indicate the anonymous lifetime{}", pluralize!(n)), + if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") }; + + diag.subdiagnostic(IndicateAnonymousLifetime { + span: insertion_span.shrink_to_hi(), + count: n, suggestion, - Applicability::MachineApplicable, - ); + }); +} + +pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( + db: &mut DiagnosticBuilder<'a, G>, + ambiguity: rustc_lint_defs::AmbiguityErrorDiag, +) { + db.span_label(ambiguity.label_span, ambiguity.label_msg); + db.note(ambiguity.note_msg); + db.span_note(ambiguity.b1_span, ambiguity.b1_note_msg); + for help_msg in ambiguity.b1_help_msgs { + db.help(help_msg); + } + db.span_note(ambiguity.b2_span, ambiguity.b2_note_msg); + for help_msg in ambiguity.b2_help_msgs { + db.help(help_msg); + } } #[derive(Clone, Copy, PartialEq, Hash, Debug)] diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs index 3b31c6d6295..6f68fb25a58 100644 --- a/compiler/rustc_errors/src/markdown/tests/term.rs +++ b/compiler/rustc_errors/src/markdown/tests/term.rs @@ -63,7 +63,7 @@ fn test_wrapping_write() { #[test] fn test_output() { // Capture `--bless` when run via ./x - let bless = std::env::var("RUSTC_BLESS").unwrap_or_default() == "1"; + let bless = std::env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); let ast = MdStream::parse_str(INPUT); let bufwtr = BufferWriter::stderr(ColorChoice::Always); let mut buffer = bufwtr.buffer(); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 4b0907cf15a..12473a2bb0b 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1147,7 +1147,7 @@ impl<'a> ExtCtxt<'a> { pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) -> ! { + pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<String>) -> ! { self.sess.parse_sess.span_diagnostic.span_bug(sp, msg); } pub fn trace_macros_diag(&mut self) { diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 264f30fb10a..7de46994434 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -643,7 +643,16 @@ impl<'a> ExtCtxt<'a> { span, name, AttrVec::new(), - ast::ItemKind::Const(ast::ConstItem { defaultness, ty, expr: Some(expr) }.into()), + ast::ItemKind::Const( + ast::ConstItem { + defaultness, + // FIXME(generic_const_items): Pass the generics as a parameter. + generics: ast::Generics::default(), + ty, + expr: Some(expr), + } + .into(), + ), ) } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 4ec5ac22e90..8658cea137a 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -365,11 +365,11 @@ impl<'a> StripUnconfigured<'a> { // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token // for `attr` when we expand it to `#[attr]` - let mut orig_trees = orig_tokens.into_trees(); + let mut orig_trees = orig_tokens.trees(); let TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _) = - orig_trees.next().unwrap() + orig_trees.next().unwrap().clone() else { - panic!("Bad tokens for attribute {:?}", attr); + panic!("Bad tokens for attribute {attr:?}"); }; let pound_span = pound_token.span; @@ -377,9 +377,9 @@ impl<'a> StripUnconfigured<'a> { if attr.style == AttrStyle::Inner { // For inner attributes, we do the same thing for the `!` in `#![some_attr]` let TokenTree::Token(bang_token @ Token { kind: TokenKind::Not, .. }, _) = - orig_trees.next().unwrap() + orig_trees.next().unwrap().clone() else { - panic!("Bad tokens for attribute {:?}", attr); + panic!("Bad tokens for attribute {attr:?}"); }; trees.push(AttrTokenTree::Token(bang_token, Spacing::Alone)); } @@ -390,7 +390,7 @@ impl<'a> StripUnconfigured<'a> { Delimiter::Bracket, item.tokens .as_ref() - .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) + .unwrap_or_else(|| panic!("Missing tokens for {item:?}")) .to_attr_token_stream(), ); trees.push(bracket_group); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 3b6db9fd39c..165c6d47c8a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -803,7 +803,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &self.cx.sess.parse_sess, sym::proc_macro_hygiene, span, - format!("custom attributes cannot be applied to {}", kind), + format!("custom attributes cannot be applied to {kind}"), ) .emit(); } @@ -1707,7 +1707,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { &UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - format!("unused attribute `{}`", attr_name), + format!("unused attribute `{attr_name}`"), BuiltinLintDiagnostics::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 03de33dc854..e060375646c 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -257,7 +257,7 @@ pub(super) fn emit_frag_parse_err( e.span_suggestion_verbose( site_span, "surround the macro invocation with `{}` to interpret the expansion as a statement", - format!("{{ {}; }}", snippet), + format!("{{ {snippet}; }}"), Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 34f998274e9..95f5bb2d2e2 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -593,7 +593,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(sess, span.into(), node_id, format!("unknown macro variable `{}`", name)); + buffer_lint(sess, span.into(), node_id, format!("unknown macro variable `{name}`")); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -626,7 +626,7 @@ fn ops_is_prefix( if i >= occurrence_ops.len() { let mut span = MultiSpan::from_span(span); span.push_span_label(binder.span, "expected repetition"); - let message = format!("variable '{}' is still repeating at this depth", name); + let message = format!("variable '{name}' is still repeating at this depth"); buffer_lint(sess, span, node_id, message); return; } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index f0e67cfd50e..05c0cd952b8 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -156,7 +156,7 @@ impl Display for MatcherLoc { MatcherLoc::MetaVarDecl { bind, kind, .. } => { write!(f, "meta-variable `${bind}")?; if let Some(kind) = kind { - write!(f, ":{}", kind)?; + write!(f, ":{kind}")?; } write!(f, "`")?; Ok(()) @@ -723,7 +723,7 @@ impl TtParser { .iter() .map(|mp| match &matcher[mp.idx] { MatcherLoc::MetaVarDecl { bind, kind: Some(kind), .. } => { - format!("{} ('{}')", kind, bind) + format!("{kind} ('{bind}')") } _ => unreachable!(), }) @@ -736,8 +736,8 @@ impl TtParser { "local ambiguity when calling macro `{}`: multiple parsing options: {}", self.macro_name, match self.next_mps.len() { - 0 => format!("built-in NTs {}.", nts), - n => format!("built-in NTs {} or {n} other option{s}.", nts, s = pluralize!(n)), + 0 => format!("built-in NTs {nts}."), + n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)), } ), ) @@ -757,7 +757,7 @@ impl TtParser { match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) { Vacant(spot) => spot.insert(res.next().unwrap()), Occupied(..) => { - return Error(span, format!("duplicated bind name: {}", bind)); + return Error(span, format!("duplicated bind name: {bind}")); } }; } else { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 42cc0a6b143..1b935058c04 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -249,7 +249,7 @@ fn expand_macro<'cx>( trace_macros_note(&mut cx.expansions, sp, msg); } - let p = Parser::new(sess, tts, false, None); + let p = Parser::new(sess, tts, None); if is_local { cx.resolver.record_macro_rule_usage(node_id, i); @@ -257,7 +257,7 @@ fn expand_macro<'cx>( // Let the context choose how to interpret the result. // Weird, but useful for X-macros. - return Box::new(ParserAnyMacro { + Box::new(ParserAnyMacro { parser: p, // Pass along the original expansion site and the name of the macro @@ -269,18 +269,17 @@ fn expand_macro<'cx>( is_trailing_mac: cx.current_expansion.is_trailing_mac, arm_span, is_local, - }); + }) } Err(CanRetry::No(_)) => { debug!("Will not retry matching as an error was emitted already"); - return DummyResult::any(sp); + DummyResult::any(sp) } Err(CanRetry::Yes) => { - // Retry and emit a better error below. + // Retry and emit a better error. + diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) } } - - diagnostics::failed_to_match_macro(cx, sp, def_span, name, arg, lhses) } pub(super) enum CanRetry { @@ -447,7 +446,7 @@ pub fn compile_declarative_macro( let create_parser = || { let body = macro_def.body.tokens.clone(); - Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS) + Parser::new(&sess.parse_sess, body, rustc_parse::MACRO_ARGUMENTS) }; let parser = create_parser(); @@ -457,8 +456,8 @@ pub fn compile_declarative_macro( match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) { Success(m) => m, Failure(()) => { - // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it with another one - // that gives us the information we need. + // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it + // with another one that gives us the information we need. // For this we need to reclone the macro body as the previous parser consumed it. let retry_parser = create_parser(); @@ -500,7 +499,7 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedTokenTree(tt) = m { let tt = mbe::quoted::parse( - TokenStream::new(vec![tt.clone()]), + &TokenStream::new(vec![tt.clone()]), true, &sess.parse_sess, def.id, @@ -524,7 +523,7 @@ pub fn compile_declarative_macro( .map(|m| { if let MatchedTokenTree(tt) = m { return mbe::quoted::parse( - TokenStream::new(vec![tt.clone()]), + &TokenStream::new(vec![tt.clone()]), false, &sess.parse_sess, def.id, @@ -554,7 +553,7 @@ pub fn compile_declarative_macro( let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules); match transparency_error { Some(TransparencyError::UnknownTransparency(value, span)) => { - diag.span_err(span, format!("unknown macro transparency: `{}`", value)); + diag.span_err(span, format!("unknown macro transparency: `{value}`")); } Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => { diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes"); @@ -1197,10 +1196,10 @@ fn check_matcher_core<'tt>( may_be = may_be ), ); - err.span_label(sp, format!("not allowed after `{}` fragments", kind)); + err.span_label(sp, format!("not allowed after `{kind}` fragments")); if kind == NonterminalKind::PatWithOr - && sess.edition.rust_2021() + && sess.edition.at_least_rust_2021() && next_token.is_token(&BinOp(token::BinOpToken::Or)) { let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( @@ -1221,8 +1220,7 @@ fn check_matcher_core<'tt>( &[] => {} &[t] => { err.note(format!( - "only {} is allowed after `{}` fragments", - t, kind, + "only {t} is allowed after `{kind}` fragments", )); } ts => { @@ -1407,9 +1405,9 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { match tt { mbe::TokenTree::Token(token) => pprust::token_to_string(&token).into(), - mbe::TokenTree::MetaVar(_, name) => format!("${}", name), - mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind), - mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name), + mbe::TokenTree::MetaVar(_, name) => format!("${name}"), + mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${name}:{kind}"), + mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${name}:"), _ => panic!( "{}", "unexpected mbe::TokenTree::{Sequence or Delimited} \ @@ -1418,6 +1416,11 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { } } -pub(super) fn parser_from_cx(sess: &ParseSess, tts: TokenStream, recovery: Recovery) -> Parser<'_> { - Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS).recovery(recovery) +pub(super) fn parser_from_cx( + sess: &ParseSess, + mut tts: TokenStream, + recovery: Recovery, +) -> Parser<'_> { + tts.desugar_doc_comments(); + Parser::new(sess, tts, rustc_parse::MACRO_ARGUMENTS).recovery(recovery) } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 40bfa3715be..6546199f5e6 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -36,7 +36,7 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ /// /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`. pub(super) fn parse( - input: tokenstream::TokenStream, + input: &tokenstream::TokenStream, parsing_patterns: bool, sess: &ParseSess, node_id: NodeId, @@ -48,7 +48,7 @@ pub(super) fn parse( // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming // additional trees if need be. - let mut trees = input.into_trees(); + let mut trees = input.trees(); while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). @@ -56,7 +56,7 @@ pub(super) fn parse( match tree { TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { let span = match trees.next() { - Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { + Some(&tokenstream::TokenTree::Token(Token { kind: token::Colon, span }, _)) => { match trees.next() { Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { Some((frag, _)) => { @@ -96,10 +96,10 @@ pub(super) fn parse( } _ => token.span, }, - tree => tree.as_ref().map_or(span, tokenstream::TokenTree::span), + tree => tree.map_or(span, tokenstream::TokenTree::span), } } - tree => tree.as_ref().map_or(start_sp, tokenstream::TokenTree::span), + tree => tree.map_or(start_sp, tokenstream::TokenTree::span), }; result.push(TokenTree::MetaVarDecl(span, ident, None)); @@ -134,9 +134,9 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &ParseSess, /// - `parsing_patterns`: same as [parse]. /// - `sess`: the parsing session. Any errors will be emitted to this session. /// - `features`: language features so we can do feature gating. -fn parse_tree( - tree: tokenstream::TokenTree, - outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>, +fn parse_tree<'a>( + tree: &'a tokenstream::TokenTree, + outer_trees: &mut impl Iterator<Item = &'a tokenstream::TokenTree>, parsing_patterns: bool, sess: &ParseSess, node_id: NodeId, @@ -146,13 +146,13 @@ fn parse_tree( // Depending on what `tree` is, we could be parsing different parts of a macro match tree { // `tree` is a `$` token. Look at the next token in `trees` - tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { + &tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _) => { // FIXME: Handle `Invisible`-delimited groups in a more systematic way // during parsing. let mut next = outer_trees.next(); - let mut trees: Box<dyn Iterator<Item = tokenstream::TokenTree>>; + let mut trees: Box<dyn Iterator<Item = &tokenstream::TokenTree>>; if let Some(tokenstream::TokenTree::Delimited(_, Delimiter::Invisible, tts)) = next { - trees = Box::new(tts.into_trees()); + trees = Box::new(tts.trees()); next = trees.next(); } else { trees = Box::new(outer_trees); @@ -160,7 +160,7 @@ fn parse_tree( match next { // `tree` is followed by a delimited set of token trees. - Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => { + Some(&tokenstream::TokenTree::Delimited(delim_span, delim, ref tts)) => { if parsing_patterns { if delim != Delimiter::Parenthesis { span_dollar_dollar_or_metavar_in_the_lhs_err( @@ -194,7 +194,7 @@ fn parse_tree( Delimiter::Parenthesis => {} _ => { let tok = pprust::token_kind_to_string(&token::OpenDelim(delim)); - let msg = format!("expected `(` or `{{`, found `{}`", tok); + let msg = format!("expected `(` or `{{`, found `{tok}`"); sess.span_diagnostic.span_err(delim_span.entire(), msg); } } @@ -228,7 +228,7 @@ fn parse_tree( } // `tree` is followed by another `$`. This is an escaped `$`. - Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { + Some(&tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }, _)) => { if parsing_patterns { span_dollar_dollar_or_metavar_in_the_lhs_err( sess, @@ -256,11 +256,11 @@ fn parse_tree( } // `tree` is an arbitrary token. Keep it. - tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token), + tokenstream::TokenTree::Token(token, _) => TokenTree::Token(token.clone()), // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to // descend into the delimited set and further parse it. - tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( + &tokenstream::TokenTree::Delimited(span, delim, ref tts) => TokenTree::Delimited( span, Delimited { delim, @@ -286,16 +286,16 @@ fn kleene_op(token: &Token) -> Option<KleeneOp> { /// - Ok(Ok((op, span))) if the next token tree is a KleeneOp /// - Ok(Err(tok, span)) if the next token tree is a token but not a KleeneOp /// - Err(span) if the next token tree is not a token -fn parse_kleene_op( - input: &mut impl Iterator<Item = tokenstream::TokenTree>, +fn parse_kleene_op<'a>( + input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>, span: Span, ) -> Result<Result<(KleeneOp, Span), Token>, Span> { match input.next() { Some(tokenstream::TokenTree::Token(token, _)) => match kleene_op(&token) { Some(op) => Ok(Ok((op, token.span))), - None => Ok(Err(token)), + None => Ok(Err(token.clone())), }, - tree => Err(tree.as_ref().map_or(span, tokenstream::TokenTree::span)), + tree => Err(tree.map_or(span, tokenstream::TokenTree::span)), } } @@ -311,8 +311,8 @@ fn parse_kleene_op( /// session `sess`. If the next one (or possibly two) tokens in `input` correspond to a Kleene /// operator and separator, then a tuple with `(separator, KleeneOp)` is returned. Otherwise, an /// error with the appropriate span is emitted to `sess` and a dummy value is returned. -fn parse_sep_and_kleene_op( - input: &mut impl Iterator<Item = tokenstream::TokenTree>, +fn parse_sep_and_kleene_op<'a>( + input: &mut impl Iterator<Item = &'a tokenstream::TokenTree>, span: Span, sess: &ParseSess, ) -> (Option<Token>, KleeneToken) { diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 8b37728b60f..bdc20882a9d 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<P<ast::Item>> { 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(), + ); }) } @@ -63,9 +69,8 @@ fn bad_path_expr_1() { #[test] fn string_to_tts_macro() { create_default_session_globals_then(|| { - let tts: Vec<_> = - string_to_stream("macro_rules! zip (($a)=>($a))".to_string()).into_trees().collect(); - let tts: &[TokenTree] = &tts[..]; + let stream = string_to_stream("macro_rules! zip (($a)=>($a))".to_string()); + let tts = &stream.trees().collect::<Vec<_>>()[..]; match tts { [ @@ -294,9 +299,7 @@ fn ttdelim_span() { .unwrap(); let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") }; - let tts: Vec<_> = mac.args.tokens.clone().into_trees().collect(); - - let span = tts.iter().rev().next().unwrap().span(); + let span = mac.args.tokens.trees().last().unwrap().span(); match sess.source_map().span_to_snippet(span) { Ok(s) => assert_eq!(&s[..], "{ body }"), diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 41b24407fa0..c617cd76e3c 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -95,7 +95,7 @@ impl base::AttrProcMacro for AttrProcMacro { |e| { let mut err = ecx.struct_span_err(span, "custom attribute panicked"); if let Some(s) = e.as_str() { - err.help(format!("message: {}", s)); + err.help(format!("message: {s}")); } err.emit() }, @@ -148,7 +148,7 @@ impl MultiItemModifier for DeriveProcMacro { Err(e) => { let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); if let Some(s) = e.as_str() { - err.help(format!("message: {}", s)); + err.help(format!("message: {s}")); } err.emit(); return ExpandResult::Ready(vec![]); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index ecd2315112a..2dc9b51a37e 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -94,10 +94,10 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre // Estimate the capacity as `stream.len()` rounded up to the next power // of two to limit the number of required reallocations. let mut trees = Vec::with_capacity(stream.len().next_power_of_two()); - let mut cursor = stream.into_trees(); + let mut cursor = stream.trees(); while let Some(tree) = cursor.next() { - let (Token { kind, span }, joint) = match tree { + let (Token { kind, span }, joint) = match tree.clone() { tokenstream::TokenTree::Delimited(span, delim, tts) => { let delimiter = pm::Delimiter::from_internal(delim); trees.push(TokenTree::Group(Group { @@ -622,7 +622,7 @@ impl server::SourceFile for Rustc<'_, '_> { impl server::Span for Rustc<'_, '_> { fn debug(&mut self, span: Self::Span) -> String { if self.ecx.ecfg.span_debug { - format!("{:?}", span) + format!("{span:?}") } else { format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) } diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 8a5e09475ff..30fa5fea407 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<SourceMap>, Arc<Mutex<Vec<u8>>>) { + 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<T, F>(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<T: Write> Write for Shared<T> { fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, 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<SpanLabel>, 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)); #[allow(rustc::untranslatable_diagnostic)] handler.span_err(msp, "foo"); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 56a2c5eff3d..bbc3d291e20 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -379,6 +379,8 @@ declare_features! ( (active, deprecated_safe, "1.61.0", Some(94978), None), /// Allows having using `suggestion` in the `#[deprecated]` attribute. (active, deprecated_suggestion, "1.61.0", Some(94785), None), + /// Allows using the `#[diagnostic]` attribute tool namespace + (active, diagnostic_namespace, "CURRENT_RUSTC_VERSION", Some(94785), None), /// Controls errors in trait implementations. (active, do_not_recommend, "1.67.0", Some(51992), None), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. @@ -422,6 +424,8 @@ declare_features! ( (incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None), /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), + /// Allows generic parameters and where-clauses on free & associated const items. + (incomplete, generic_const_items, "CURRENT_RUSTC_VERSION", Some(113521), None), /// Allows using `..=X` as a patterns in slices. (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None), /// Allows `if let` guard in match arms. @@ -445,7 +449,7 @@ declare_features! ( // Allows setting the threshold for the `large_assignments` lint. (active, large_assignments, "1.52.0", Some(83518), None), /// Allow to have type alias types for inter-crate use. - (active, lazy_type_alias, "1.72.0", Some(112792), None), + (incomplete, lazy_type_alias, "1.72.0", Some(112792), None), /// Allows `if/while p && let q = r && ...` chains. (active, let_chains, "1.37.0", Some(53667), None), /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 35ef30114b7..6b76e16825f 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2664,10 +2664,19 @@ pub struct OpaqueTy<'hir> { pub generics: &'hir Generics<'hir>, pub bounds: GenericBounds<'hir>, pub origin: OpaqueTyOrigin, - // Opaques have duplicated lifetimes, this mapping connects the original lifetime with the copy - // so we can later generate bidirectional outlives predicates to enforce that these lifetimes - // stay in sync. - pub lifetime_mapping: &'hir [(Lifetime, LocalDefId)], + /// Return-position impl traits (and async futures) must "reify" any late-bound + /// lifetimes that are captured from the function signature they originate from. + /// + /// This is done by generating a new early-bound lifetime parameter local to the + /// opaque which is substituted in the function signature with the late-bound + /// lifetime. + /// + /// This mapping associated a captured lifetime (first parameter) with the new + /// early-bound lifetime that was generated for the opaque. + pub lifetime_mapping: Option<&'hir [(Lifetime, LocalDefId)]>, + /// Whether the opaque is a return-position impl trait (or async future) + /// originating from a trait method. This makes it so that the opaque is + /// lowered as an associated type. pub in_trait: bool, } @@ -3004,8 +3013,7 @@ pub struct FieldDef<'hir> { impl FieldDef<'_> { // Still necessary in couple of places pub fn is_positional(&self) -> bool { - let first = self.ident.as_str().as_bytes()[0]; - (b'0'..=b'9').contains(&first) + self.ident.as_str().as_bytes()[0].is_ascii_digit() } } @@ -3122,9 +3130,9 @@ impl<'hir> Item<'hir> { } /// Expect an [`ItemKind::Const`] or panic. #[track_caller] - pub fn expect_const(&self) -> (&'hir Ty<'hir>, BodyId) { - let ItemKind::Const(ty, body) = self.kind else { self.expect_failed("a constant") }; - (ty, body) + pub fn expect_const(&self) -> (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId) { + let ItemKind::Const(ty, gen, body) = self.kind else { self.expect_failed("a constant") }; + (ty, gen, body) } /// Expect an [`ItemKind::Fn`] or panic. #[track_caller] @@ -3311,7 +3319,7 @@ pub enum ItemKind<'hir> { /// A `static` item. Static(&'hir Ty<'hir>, Mutability, BodyId), /// A `const` item. - Const(&'hir Ty<'hir>, BodyId), + Const(&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId), /// A function declaration. Fn(FnSig<'hir>, &'hir Generics<'hir>, BodyId), /// A MBE macro definition (`macro_rules!` or `macro`). @@ -3364,6 +3372,7 @@ impl ItemKind<'_> { Some(match *self { ItemKind::Fn(_, ref generics, _) | ItemKind::TyAlias(_, ref generics) + | ItemKind::Const(_, ref generics, _) | ItemKind::OpaqueTy(OpaqueTy { ref generics, .. }) | ItemKind::Enum(_, ref generics) | ItemKind::Struct(_, ref generics) @@ -3559,7 +3568,9 @@ impl<'hir> OwnerNode<'hir> { match self { OwnerNode::Item(Item { kind: - ItemKind::Static(_, _, body) | ItemKind::Const(_, body) | ItemKind::Fn(_, _, body), + ItemKind::Static(_, _, body) + | ItemKind::Const(_, _, body) + | ItemKind::Fn(_, _, body), .. }) | OwnerNode::TraitItem(TraitItem { @@ -3762,9 +3773,9 @@ impl<'hir> Node<'hir> { pub fn ty(self) -> Option<&'hir Ty<'hir>> { match self { Node::Item(it) => match it.kind { - ItemKind::TyAlias(ty, _) | ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => { - Some(ty) - } + ItemKind::TyAlias(ty, _) + | ItemKind::Static(ty, _, _) + | ItemKind::Const(ty, _, _) => Some(ty), _ => None, }, Node::TraitItem(it) => match it.kind { @@ -3792,7 +3803,9 @@ impl<'hir> Node<'hir> { match self { Node::Item(Item { kind: - ItemKind::Static(_, _, body) | ItemKind::Const(_, body) | ItemKind::Fn(_, _, body), + ItemKind::Static(_, _, body) + | ItemKind::Const(_, _, body) + | ItemKind::Fn(_, _, body), .. }) | Node::TraitItem(TraitItem { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 347c1f4637f..a8a94e6a476 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -467,11 +467,17 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) { ItemKind::Use(ref path, _) => { visitor.visit_use(path, item.hir_id()); } - ItemKind::Static(ref typ, _, body) | ItemKind::Const(ref typ, body) => { + ItemKind::Static(ref typ, _, body) => { visitor.visit_id(item.hir_id()); visitor.visit_ty(typ); visitor.visit_nested_body(body); } + ItemKind::Const(ref typ, ref generics, body) => { + visitor.visit_id(item.hir_id()); + visitor.visit_ty(typ); + visitor.visit_generics(generics); + visitor.visit_nested_body(body); + } ItemKind::Fn(ref sig, ref generics, body_id) => { visitor.visit_id(item.hir_id()); visitor.visit_fn( diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index 7f4927bbb98..bd311c98fac 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -197,7 +197,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - err.span_label(span, format!("associated type `{}` not found", assoc_name)); + err.span_label(span, format!("associated type `{assoc_name}` not found")); err.emit() } @@ -393,7 +393,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .into_iter() .map(|error| error.root_obligation.predicate) .filter_map(format_pred) - .map(|(p, _)| format!("`{}`", p)) + .map(|(p, _)| format!("`{p}`")) .collect(); bounds.sort(); bounds.dedup(); @@ -652,7 +652,7 @@ pub(crate) fn fn_trait_to_string( } .map(|s| { // `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma - if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) } + if parenthesized || s.is_empty() { format!("({s})") } else { format!("({s},)") } }) .ok(), _ => None, diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index e81c61d80ed..1372cc896be 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -81,7 +81,7 @@ fn generic_arg_mismatch_err( err.span_suggestion( tcx.def_span(src_def_id), "consider changing this type parameter to a const parameter", - format!("const {}: {}", param_name, param_type), + format!("const {param_name}: {param_type}"), Applicability::MaybeIncorrect, ); }; @@ -102,7 +102,7 @@ fn generic_arg_mismatch_err( err.span_suggestion( arg.span(), "array type provided where a `usize` was expected, try", - format!("{{ {} }}", snippet), + format!("{{ {snippet} }}"), Applicability::MaybeIncorrect, ); } @@ -130,7 +130,7 @@ fn generic_arg_mismatch_err( } else { (arg.descr(), param.kind.descr()) }; - err.note(format!("{} arguments must be provided before {} arguments", first, last)); + err.note(format!("{first} arguments must be provided before {last} arguments")); if let Some(help) = help { err.help(help); } @@ -304,7 +304,7 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>( "reorder the arguments: {}: `<{}>`", param_types_present .into_iter() - .map(|ord| format!("{}s", ord)) + .map(|ord| format!("{ord}s")) .collect::<Vec<String>>() .join(", then "), ordered_params diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index 05a3ab63d5c..1bd1270beaf 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -34,9 +34,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let param_name = generics.params.next_type_param_name(None); let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { - (span, format!(", {}: {}", param_name, impl_trait_name)) + (span, format!(", {param_name}: {impl_trait_name}")) } else { - (generics.span, format!("<{}: {}>", param_name, impl_trait_name)) + (generics.span, format!("<{param_name}: {impl_trait_name}>")) }; diag.multipart_suggestion( format!("alternatively use a blanket \ @@ -86,7 +86,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { )); } - if self_ty.span.edition().rust_2021() { + if self_ty.span.edition().at_least_rust_2021() { let msg = "trait objects must include the `dyn` keyword"; let label = "add `dyn` keyword before this trait"; let mut diag = diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index ecbbfd92539..3235a9ceba1 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -1128,7 +1128,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty_param_name ) }; - err.span_label(span, format!("ambiguous associated type `{}`", assoc_name)); + err.span_label(span, format!("ambiguous associated type `{assoc_name}`")); let mut where_bounds = vec![]; for bound in bounds { @@ -1407,7 +1407,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { _ => { let reported = if variant_resolution.is_some() { // Variant in type position - let msg = format!("expected type, found variant `{}`", assoc_ident); + let msg = format!("expected type, found variant `{assoc_ident}`"); tcx.sess.span_err(span, msg) } else if qself_ty.is_enum() { let mut err = struct_span_err!( @@ -1438,12 +1438,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } else { err.span_label( assoc_ident.span, - format!("variant not found in `{}`", qself_ty), + format!("variant not found in `{qself_ty}`"), ); } if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) { - err.span_label(sp, format!("variant `{}` not found here", assoc_ident)); + err.span_label(sp, format!("variant `{assoc_ident}` not found here")); } err.emit() @@ -2750,7 +2750,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) | ty::BrEnv => { "an anonymous lifetime".to_string() } - ty::BrNamed(_, name) => format!("lifetime `{}`", name), + ty::BrNamed(_, name) => format!("lifetime `{name}`"), }; let mut err = generate_err(&br_name); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d9e14096954..73627a818e5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -19,11 +19,13 @@ use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; use rustc_middle::hir::nested_filter; use rustc_middle::middle::stability::EvalResult; use rustc_middle::traits::DefiningAnchor; +use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; use rustc_middle::ty::util::{Discr, IntTypeExt}; use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{ - self, AdtDef, ParamEnv, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self, AdtDef, ParamEnv, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, }; use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_span::symbol::sym; @@ -34,6 +36,7 @@ use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplem use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _}; +use rustc_type_ir::fold::TypeFoldable; use std::ops::ControlFlow; @@ -342,7 +345,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( err.span_suggestion( span, "consider spelling out the type instead", - name.unwrap_or_else(|| format!("{:?}", ty)), + name.unwrap_or_else(|| format!("{ty:?}")), Applicability::MaybeIncorrect, ); } @@ -437,7 +440,7 @@ fn check_opaque_meets_bounds<'tcx>( // hidden type is well formed even without those bounds. let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into()))); - ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate)); + ocx.register_obligation(Obligation::new(tcx, misc_cause.clone(), param_env, predicate)); // Check that all obligations are satisfied by the implementation's // version. @@ -464,11 +467,165 @@ fn check_opaque_meets_bounds<'tcx>( ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?; } } - // Clean up after ourselves - let _ = infcx.take_opaque_types(); + // Check that any hidden types found during wf checking match the hidden types that `type_of` sees. + for (key, mut ty) in infcx.take_opaque_types() { + ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty); + sanity_check_found_hidden_type(tcx, key, ty.hidden_type, defining_use_anchor, origin)?; + } Ok(()) } +fn sanity_check_found_hidden_type<'tcx>( + tcx: TyCtxt<'tcx>, + key: ty::OpaqueTypeKey<'tcx>, + mut ty: ty::OpaqueHiddenType<'tcx>, + defining_use_anchor: LocalDefId, + origin: &hir::OpaqueTyOrigin, +) -> Result<(), ErrorGuaranteed> { + if ty.ty.is_ty_var() { + // Nothing was actually constrained. + return Ok(()); + } + if let ty::Alias(ty::Opaque, alias) = ty.ty.kind() { + if alias.def_id == key.def_id.to_def_id() && alias.args == key.args { + // Nothing was actually constrained, this is an opaque usage that was + // only discovered to be opaque after inference vars resolved. + return Ok(()); + } + } + // Closures frequently end up containing erased lifetimes in their final representation. + // These correspond to lifetime variables that never got resolved, so we patch this up here. + ty.ty = ty.ty.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |t| t, + ct_op: |c| c, + lt_op: |l| match l.kind() { + RegionKind::ReVar(_) => tcx.lifetimes.re_erased, + _ => l, + }, + }); + // Get the hidden type. + let mut hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); + if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin { + if hidden_ty != ty.ty { + hidden_ty = find_and_apply_rpit_args( + tcx, + hidden_ty, + defining_use_anchor.to_def_id(), + key.def_id.to_def_id(), + )?; + } + } + + // If the hidden types differ, emit a type mismatch diagnostic. + if hidden_ty == ty.ty { + Ok(()) + } else { + let span = tcx.def_span(key.def_id); + let other = ty::OpaqueHiddenType { ty: hidden_ty, span }; + Err(ty.report_mismatch(&other, key.def_id, tcx).emit()) + } +} + +/// In case it is in a nested opaque type, find that opaque type's +/// usage in the function signature and use the generic arguments from the usage site. +/// We need to do because RPITs ignore the lifetimes of the function, +/// as they have their own copies of all the lifetimes they capture. +/// So the only way to get the lifetimes represented in terms of the function, +/// is to look how they are used in the function signature (or do some other fancy +/// recording of this mapping at ast -> hir lowering time). +/// +/// As an example: +/// ```text +/// trait Id { +/// type Assoc; +/// } +/// impl<'a> Id for &'a () { +/// type Assoc = &'a (); +/// } +/// fn func<'a>(x: &'a ()) -> impl Id<Assoc = impl Sized + 'a> { x } +/// // desugared to +/// fn func<'a>(x: &'a () -> Outer<'a> where <Outer<'a> as Id>::Assoc = Inner<'a> { +/// // Note that in contrast to other nested items, RPIT type aliases can +/// // access their parents' generics. +/// +/// // hidden type is `&'aDupOuter ()` +/// // During wfcheck the hidden type of `Inner<'aDupOuter>` is `&'a ()`, but +/// // `typeof(Inner<'aDupOuter>) = &'aDupOuter ()`. +/// // So we walk the signature of `func` to find the use of `Inner<'a>` +/// // and then use that to replace the lifetimes in the hidden type, obtaining +/// // `&'a ()`. +/// type Outer<'aDupOuter> = impl Id<Assoc = Inner<'aDupOuter>>; +/// +/// // hidden type is `&'aDupInner ()` +/// type Inner<'aDupInner> = impl Sized + 'aDupInner; +/// +/// x +/// } +/// ``` +fn find_and_apply_rpit_args<'tcx>( + tcx: TyCtxt<'tcx>, + mut hidden_ty: Ty<'tcx>, + function: DefId, + opaque: DefId, +) -> Result<Ty<'tcx>, ErrorGuaranteed> { + // Find use of the RPIT in the function signature and thus find the right args to + // convert it into the parameter space of the function signature. This is needed, + // because that's what `type_of` returns, against which we compare later. + let ret = tcx.fn_sig(function).instantiate_identity().output(); + struct Visitor<'tcx> { + tcx: TyCtxt<'tcx>, + opaque: DefId, + seen: FxHashSet<DefId>, + } + impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> { + type BreakTy = GenericArgsRef<'tcx>; + + #[instrument(level = "trace", skip(self), ret)] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + trace!("{:#?}", t.kind()); + match t.kind() { + ty::Alias(ty::Opaque, alias) => { + trace!(?alias.def_id); + if alias.def_id == self.opaque { + return ControlFlow::Break(alias.args); + } else if self.seen.insert(alias.def_id) { + for clause in self + .tcx + .explicit_item_bounds(alias.def_id) + .iter_instantiated_copied(self.tcx, alias.args) + { + trace!(?clause); + clause.visit_with(self)?; + } + } + } + ty::Alias(ty::Weak, alias) => { + self.tcx + .type_of(alias.def_id) + .instantiate(self.tcx, alias.args) + .visit_with(self)?; + } + _ => (), + } + + t.super_visit_with(self) + } + } + if let ControlFlow::Break(args) = + ret.visit_with(&mut Visitor { tcx, opaque, seen: Default::default() }) + { + trace!(?args); + trace!("expected: {hidden_ty:#?}"); + hidden_ty = ty::EarlyBinder::bind(hidden_ty).instantiate(tcx, args); + trace!("expected: {hidden_ty:#?}"); + } else { + tcx.sess + .delay_span_bug(tcx.def_span(function), format!("{ret:?} does not contain {opaque:?}")); + } + Ok(hidden_ty) +} + fn is_enum_of_nonnullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, adt_def: AdtDef<'tcx>, @@ -626,7 +783,7 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) { "replace the {} parameters with concrete {}{}", kinds, kinds_pl, - egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(), + egs.map(|egs| format!(" like `{egs}`")).unwrap_or_default(), ), ) .emit(); @@ -711,7 +868,7 @@ pub(super) fn check_specialization_validity<'tcx>( } else { tcx.sess.delay_span_bug( DUMMY_SP, - format!("parent item: {:?} not marked as default", parent_impl), + format!("parent item: {parent_impl:?} not marked as default"), ); } } @@ -1078,9 +1235,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // We are currently checking the type this field came from, so it must be local let span = tcx.hir().span_if_local(field.did).unwrap(); let zst = layout.is_ok_and(|layout| layout.is_zst()); - let align1 = layout.is_ok_and(|layout| layout.align.abi.bytes() == 1); + let align = layout.ok().map(|layout| layout.align.abi.bytes()); if !zst { - return (span, zst, align1, None); + return (span, zst, align, None); } fn check_non_exhaustive<'tcx>( @@ -1115,12 +1272,12 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) } } - (span, zst, align1, check_non_exhaustive(tcx, ty).break_value()) + (span, zst, align, check_non_exhaustive(tcx, ty).break_value()) }); let non_zst_fields = field_infos .clone() - .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None }); + .filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None }); let non_zst_count = non_zst_fields.clone().count(); if non_zst_count >= 2 { bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did())); @@ -1128,17 +1285,26 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) let incompatible_zst_fields = field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count(); let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2; - for (span, zst, align1, non_exhaustive) in field_infos { - if zst && !align1 { - struct_span_err!( + for (span, zst, align, non_exhaustive) in field_infos { + if zst && align != Some(1) { + let mut err = struct_span_err!( tcx.sess, span, E0691, "zero-sized field in transparent {} has alignment larger than 1", adt.descr(), - ) - .span_label(span, "has alignment larger than 1") - .emit(); + ); + + if let Some(align_bytes) = align { + err.span_label( + span, + format!("has alignment of {align_bytes}, which is larger than 1"), + ); + } else { + err.span_label(span, "may have alignment larger than 1"); + } + + err.emit(); } if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive { tcx.struct_span_lint_hir( diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 89877280a73..a8c66ff9001 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -76,7 +76,7 @@ fn check_method_is_structurally_compatible<'tcx>( Ok(()) } -/// This function is best explained by example. Consider a trait with it's implementation: +/// This function is best explained by example. Consider a trait with its implementation: /// /// ```rust /// trait Trait<'t, T> { @@ -120,7 +120,7 @@ fn check_method_is_structurally_compatible<'tcx>( /// types: /// /// ```rust,ignore (pseudo-Rust) -/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo +/// <'b> fn(t: &'i0 U0, m: &'b N0) -> Foo /// ``` /// /// We now want to extract and substitute the type of the *trait* @@ -137,7 +137,7 @@ fn check_method_is_structurally_compatible<'tcx>( /// Applying this to the trait method type yields: /// /// ```rust,ignore (pseudo-Rust) -/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo +/// <'a> fn(t: &'i0 U0, m: &'a N0) -> Foo /// ``` /// /// This type is also the same but the name of the bound region (`'a` @@ -219,11 +219,7 @@ fn compare_method_predicate_entailment<'tcx>( // The key step here is to update the caller_bounds's predicates to be // the new hybrid bounds we computed. let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id); - let param_env = ty::ParamEnv::new( - tcx.mk_clauses(&hybrid_preds.predicates), - Reveal::UserFacing, - hir::Constness::NotConst, - ); + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); let infcx = &tcx.infer_ctxt().build(); @@ -262,8 +258,6 @@ fn compare_method_predicate_entailment<'tcx>( // type. // Compute placeholder form of impl and trait method tys. - let tcx = infcx.tcx; - let mut wf_tys = FxIndexSet::default(); let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars( @@ -760,7 +754,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( ); ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?; - let mut collected_tys = FxHashMap::default(); + let mut remapped_types = FxHashMap::default(); for (def_id, (ty, args)) in collected_types { match infcx.fully_resolve((ty, args)) { Ok((ty, args)) => { @@ -810,19 +804,37 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( Ok(ty) => ty, Err(guar) => Ty::new_error(tcx, guar), }; - collected_tys.insert(def_id, ty::EarlyBinder::bind(ty)); + remapped_types.insert(def_id, ty::EarlyBinder::bind(ty)); } Err(err) => { let reported = tcx.sess.delay_span_bug( return_span, format!("could not fully resolve: {ty} => {err:?}"), ); - collected_tys.insert(def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, reported))); + remapped_types.insert(def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, reported))); } } } - Ok(&*tcx.arena.alloc(collected_tys)) + // We may not collect all RPITITs that we see in the HIR for a trait signature + // because an RPITIT was located within a missing item. Like if we have a sig + // returning `-> Missing<impl Sized>`, that gets converted to `-> [type error]`, + // and when walking through the signature we end up never collecting the def id + // of the `impl Sized`. Insert that here, so we don't ICE later. + for assoc_item in tcx.associated_types_for_impl_traits_in_associated_fn(trait_m.def_id) { + if !remapped_types.contains_key(assoc_item) { + remapped_types.insert( + *assoc_item, + ty::EarlyBinder::bind(Ty::new_error_with_message( + tcx, + return_span, + "missing synthetic item for RPITIT", + )), + ); + } + } + + Ok(&*tcx.arena.alloc(remapped_types)) } struct ImplTraitInTraitCollector<'a, 'tcx> { @@ -1672,19 +1684,19 @@ fn compare_synthetic_generics<'tcx>( /// ```rust,ignore (pseudo-Rust) /// trait Foo { /// fn foo<const N: u8>(); -/// type bar<const N: u8>; +/// type Bar<const N: u8>; /// fn baz<const N: u32>(); -/// type blah<T>; +/// type Blah<T>; /// } /// /// impl Foo for () { /// fn foo<const N: u64>() {} /// //~^ error -/// type bar<const N: u64> {} +/// type Bar<const N: u64> = (); /// //~^ error /// fn baz<T>() {} /// //~^ error -/// type blah<const N: i64> = u32; +/// type Blah<const N: i64> = u32; /// //~^ error /// } /// ``` @@ -1744,7 +1756,7 @@ fn compare_generic_param_kinds<'tcx>( tcx.type_of(param.def_id).instantiate_identity() ) } - Type { .. } => format!("{} type parameter", prefix), + Type { .. } => format!("{prefix} type parameter"), Lifetime { .. } => unreachable!(), }; @@ -1773,36 +1785,82 @@ pub(super) fn compare_impl_const_raw( let trait_const_item = tcx.associated_item(trait_const_item_def); let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity(); - debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id()); + debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref); - let infcx = tcx.infer_ctxt().build(); - let param_env = tcx.param_env(impl_const_item_def.to_def_id()); - let ocx = ObligationCtxt::new(&infcx); + compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?; + compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?; + compare_const_predicate_entailment(tcx, impl_const_item, trait_const_item, impl_trait_ref) +} + +/// The equivalent of [compare_method_predicate_entailment], but for associated constants +/// instead of associated functions. +// FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`. +fn compare_const_predicate_entailment<'tcx>( + tcx: TyCtxt<'tcx>, + impl_ct: ty::AssocItem, + trait_ct: ty::AssocItem, + impl_trait_ref: ty::TraitRef<'tcx>, +) -> Result<(), ErrorGuaranteed> { + let impl_ct_def_id = impl_ct.def_id.expect_local(); + let impl_ct_span = tcx.def_span(impl_ct_def_id); // The below is for the most part highly similar to the procedure // for methods above. It is simpler in many respects, especially // because we shouldn't really have to deal with lifetimes or // predicates. In fact some of this should probably be put into // shared functions because of DRY violations... - let trait_to_impl_args = impl_trait_ref.args; + let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id); + let trait_to_impl_args = + impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args); // Create a parameter environment that represents the implementation's // method. // Compute placeholder form of impl and trait const tys. - let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()).instantiate_identity(); - let trait_ty = tcx.type_of(trait_const_item_def).instantiate(tcx, trait_to_impl_args); - let mut cause = ObligationCause::new( - impl_c_span, - impl_const_item_def, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_const_item_def, - trait_item_def_id: trait_const_item_def, - kind: impl_const_item.kind, - }, + let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity(); + + let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args); + let code = ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_ct_def_id, + trait_item_def_id: trait_ct.def_id, + kind: impl_ct.kind, + }; + let mut cause = ObligationCause::new(impl_ct_span, impl_ct_def_id, code.clone()); + + let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id); + let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id); + + check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?; + + // The predicates declared by the impl definition, the trait and the + // associated const in the trait are assumed. + let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap()); + let mut hybrid_preds = impl_predicates.instantiate_identity(tcx); + hybrid_preds.predicates.extend( + trait_ct_predicates + .instantiate_own(tcx, trait_to_impl_args) + .map(|(predicate, _)| predicate), + ); + + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); + let param_env = traits::normalize_param_env_or_error( + tcx, + param_env, + ObligationCause::misc(impl_ct_span, impl_ct_def_id), ); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + + let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args); + for (predicate, span) in impl_ct_own_bounds { + let cause = ObligationCause::misc(span, impl_ct_def_id); + let predicate = ocx.normalize(&cause, param_env, predicate); + + let cause = ObligationCause::new(span, impl_ct_def_id, code.clone()); + ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate)); + } + // There is no "body" here, so just pass dummy id. let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -1821,7 +1879,7 @@ pub(super) fn compare_impl_const_raw( ); // Locate the Span containing just the type of the offending impl - let (ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).expect_const(); + let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const(); cause.span = ty.span; let mut diag = struct_span_err!( @@ -1829,12 +1887,12 @@ pub(super) fn compare_impl_const_raw( cause.span, E0326, "implemented const `{}` has an incompatible type for trait", - trait_const_item.name + trait_ct.name ); - let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| { + let trait_c_span = trait_ct.def_id.as_local().map(|trait_ct_def_id| { // Add a label to the Span containing just the type of the const - let (ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).expect_const(); + let (ty, _) = tcx.hir().expect_trait_item(trait_ct_def_id).expect_const(); ty.span }); @@ -1861,7 +1919,7 @@ pub(super) fn compare_impl_const_raw( } let outlives_env = OutlivesEnvironment::new(param_env); - ocx.resolve_regions_and_report_errors(impl_const_item_def, &outlives_env) + ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env) } pub(super) fn compare_impl_ty<'tcx>( @@ -1903,7 +1961,7 @@ fn compare_type_predicate_entailment<'tcx>( return Ok(()); } - // This `HirId` should be used for the `body_id` field on each + // This `DefId` should be used for the `body_id` field on each // `ObligationCause` (and the `FnCtxt`). This is what // `regionck_item` expects. let impl_ty_def_id = impl_ty.def_id.expect_local(); @@ -1922,12 +1980,8 @@ fn compare_type_predicate_entailment<'tcx>( debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds); let impl_ty_span = tcx.def_span(impl_ty_def_id); - let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id); - let param_env = ty::ParamEnv::new( - tcx.mk_clauses(&hybrid_preds.predicates), - Reveal::UserFacing, - hir::Constness::NotConst, - ); + let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id); + let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); let infcx = tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(&infcx); @@ -1971,7 +2025,7 @@ fn compare_type_predicate_entailment<'tcx>( /// /// trait X { type Y: Copy } impl X for T { type Y = S; } /// -/// We are able to normalize `<T as X>::U` to `S`, and so when we check the +/// We are able to normalize `<T as X>::Y` to `S`, and so when we check the /// impl is well-formed we have to prove `S: Copy`. /// /// For default associated types the normalization is not possible (the value @@ -2102,7 +2156,7 @@ pub(super) fn check_type_bounds<'tcx>( .to_predicate(tcx), ), }; - ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing, param_env.constness()) + ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing) }; debug!(?normalize_param_env); diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index 4ac3a7c23ae..dda3f742569 100644 --- a/compiler/rustc_hir_analysis/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -129,9 +129,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // We don't need to normalize this param-env or anything, since we're only // substituting it with free params, so no additional param-env normalization // can occur on top of what has been done in the param_env query itself. - let param_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id)) - .instantiate(tcx, adt_to_impl_args) - .with_constness(tcx.constness(drop_impl_def_id)); + let param_env = + ty::EarlyBinder::bind(tcx.param_env(adt_def_id)).instantiate(tcx, adt_to_impl_args); for (pred, span) in tcx.predicates_of(drop_impl_def_id).instantiate_identity(tcx) { let normalize_cause = traits::ObligationCause::misc(span, adt_def_id); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index d5c9840887a..064021b1ea4 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -134,7 +134,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`, /// and in `library/core/src/intrinsics.rs`. pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { - let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{}", n))); + let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{n}"))); let intrinsic_id = it.owner_id.to_def_id(); let intrinsic_name = tcx.item_name(intrinsic_id); let name_str = intrinsic_name.as_str(); @@ -494,7 +494,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { /// Type-check `extern "platform-intrinsic" { ... }` functions. pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let param = |n| { - let name = Symbol::intern(&format!("P{}", n)); + let name = Symbol::intern(&format!("P{n}")); Ty::new_param(tcx, n, name) }; @@ -521,6 +521,10 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)), sym::simd_arith_offset => (2, vec![param(0), param(1)], param(0)), sym::simd_neg + | sym::simd_bswap + | sym::simd_bitreverse + | sym::simd_ctlz + | sym::simd_cttz | sym::simd_fsqrt | sym::simd_fsin | sym::simd_fcos diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 8423a9550ba..b0dd5e5787d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -211,7 +211,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // register class is usable at all. if let Some(feature) = feature { if !target_features.contains(feature) { - let msg = format!("`{}` target feature is not enabled", feature); + let msg = format!("`{feature}` target feature is not enabled"); let mut err = self.tcx.sess.struct_span_err(expr.span, msg); err.note(format!( "this is required to use type `{}` with register class `{}`", diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index def7a3a9d88..4cf3587327d 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -214,7 +214,7 @@ fn missing_items_err( trait_item, tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(), ); - let code = format!("{}{}\n{}", padding, snippet, padding); + let code = format!("{padding}{snippet}\n{padding}"); if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { missing_trait_item_label .push(errors::MissingTraitItemLabel { span, item: trait_item.name }); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 4e194f1c381..ad886ce82b1 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -75,12 +75,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { self.body_def_id, ObligationCauseCode::WellFormed(loc), ); - // for a type to be WF, we do not need to check if const trait predicates satisfy. - let param_env = self.param_env.without_const(); self.ocx.register_obligation(traits::Obligation::new( self.tcx(), cause, - param_env, + self.param_env, ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), )); } @@ -248,8 +246,11 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => {} hir::ItemKind::TyAlias(hir_ty, ..) => { - if tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() { - // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);` + if tcx.features().lazy_type_alias + || tcx.type_of(item.owner_id).skip_binder().has_opaque_types() + { + // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. + // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); } } @@ -286,6 +287,17 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { }; check_object_unsafe_self_trait_by_name(tcx, trait_item); check_associated_item(tcx, def_id, span, method_sig); + + if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) { + for &assoc_ty_def_id in tcx.associated_types_for_impl_traits_in_associated_fn(def_id) { + check_associated_item( + tcx, + assoc_ty_def_id.expect_local(), + tcx.def_span(assoc_ty_def_id), + None, + ); + } + } } /// Require that the user writes where clauses on GATs for the implicit @@ -472,8 +484,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe let bound = if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" }; err.note(format!( - "{} currently required to ensure that impls have maximum flexibility", - bound + "{bound} currently required to ensure that impls have maximum flexibility" )); err.note( "we are soliciting feedback, see issue #87479 \ @@ -505,7 +516,7 @@ fn augment_param_env<'tcx>( ); // FIXME(compiler-errors): Perhaps there is a case where we need to normalize this // i.e. traits::normalize_param_env_or_error - ty::ParamEnv::new(bounds, param_env.reveal(), param_env.constness()) + ty::ParamEnv::new(bounds, param_env.reveal()) } /// We use the following trait as an example throughout this function. @@ -989,7 +1000,7 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b let ty = tcx.erase_regions(ty); if ty.has_infer() { tcx.sess - .delay_span_bug(item.span, format!("inference variables in {:?}", ty)); + .delay_span_bug(item.span, format!("inference variables in {ty:?}")); // Just treat unresolved type expression as if it needs drop. true } else { @@ -1416,7 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| { traits::wf::predicate_obligations( infcx, - wfcx.param_env.without_const(), + wfcx.param_env, wfcx.body_def_id, p.as_predicate(), sp, @@ -1469,13 +1480,6 @@ fn check_fn_or_method<'tcx>( check_where_clauses(wfcx, span, def_id); - check_return_position_impl_trait_in_trait_bounds( - wfcx, - def_id, - sig.output(), - hir_decl.output.span(), - ); - if sig.abi == Abi::RustCall { let span = tcx.def_span(def_id); let has_implicit_self = hir_decl.implicit_self != hir::ImplicitSelfKind::None; @@ -1510,87 +1514,6 @@ fn check_fn_or_method<'tcx>( } } -/// Basically `check_associated_type_bounds`, but separated for now and should be -/// deduplicated when RPITITs get lowered into real associated items. -#[tracing::instrument(level = "trace", skip(wfcx))] -fn check_return_position_impl_trait_in_trait_bounds<'tcx>( - wfcx: &WfCheckingCtxt<'_, 'tcx>, - fn_def_id: LocalDefId, - fn_output: Ty<'tcx>, - span: Span, -) { - let tcx = wfcx.tcx(); - let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id()) else { - return; - }; - if assoc_item.container != ty::AssocItemContainer::TraitContainer { - return; - } - fn_output.visit_with(&mut ImplTraitInTraitFinder { - wfcx, - fn_def_id, - depth: ty::INNERMOST, - seen: FxHashSet::default(), - }); -} - -// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): Even with the new lowering -// strategy, we can't just call `check_associated_item` on the new RPITITs, -// because tests like `tests/ui/async-await/in-trait/implied-bounds.rs` will fail. -// That's because we need to check that the bounds of the RPITIT hold using -// the special args that we create during opaque type lowering, otherwise we're -// getting a bunch of early bound and free regions mixed up... Haven't looked too -// deep into this, though. -struct ImplTraitInTraitFinder<'a, 'tcx> { - wfcx: &'a WfCheckingCtxt<'a, 'tcx>, - fn_def_id: LocalDefId, - depth: ty::DebruijnIndex, - seen: FxHashSet<DefId>, -} -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> { - type BreakTy = !; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<!> { - let tcx = self.wfcx.tcx(); - if let ty::Alias(ty::Opaque, unshifted_opaque_ty) = *ty.kind() - && self.seen.insert(unshifted_opaque_ty.def_id) - && let Some(opaque_def_id) = unshifted_opaque_ty.def_id.as_local() - && let origin = tcx.opaque_type_origin(opaque_def_id) - && let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = origin - && source == self.fn_def_id - { - let opaque_ty = tcx.fold_regions(unshifted_opaque_ty, |re, _depth| { - match re.kind() { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReError(_) | ty::ReStatic => re, - r => bug!("unexpected region: {r:?}"), - } - }); - for (bound, bound_span) in tcx - .explicit_item_bounds(opaque_ty.def_id) - .iter_instantiated_copied(tcx, opaque_ty.args) - { - let bound = self.wfcx.normalize(bound_span, None, bound); - self.wfcx.register_obligations(traits::wf::predicate_obligations( - self.wfcx.infcx, - self.wfcx.param_env, - self.wfcx.body_def_id, - bound.as_predicate(), - bound_span, - )); - // Set the debruijn index back to innermost here, since we already eagerly - // shifted the args that we use to generate these bounds. This is unfortunately - // subtly different behavior than the `ImplTraitInTraitFinder` we use in `param_env`, - // but that function doesn't actually need to normalize the bound it's visiting - // (whereas we have to do so here)... - let old_depth = std::mem::replace(&mut self.depth, ty::INNERMOST); - bound.visit_with(self); - self.depth = old_depth; - } - } - ty.super_visit_with(self) - } -} - const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \ `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \ of the previous types except `Self`)"; @@ -1863,8 +1786,7 @@ fn report_bivariance( if matches!(param.kind, hir::GenericParamKind::Type { .. }) && !has_explicit_bounds { err.help(format!( - "if you intended `{0}` to be a const parameter, use `const {0}: usize` instead", - param_name + "if you intended `{param_name}` to be a const parameter, use `const {param_name}: usize` instead" )); } err.emit() diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 5318e637fc7..9ad73eeffc6 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -1,11 +1,16 @@ use rustc_data_structures::unord::{ExtendUnord, UnordSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint; -pub fn check_crate(tcx: TyCtxt<'_>) { - let mut used_trait_imports: UnordSet<LocalDefId> = Default::default(); +pub fn provide(providers: &mut Providers) { + *providers = Providers { check_unused_traits, ..*providers }; +} + +fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { + let mut used_trait_imports = UnordSet::<LocalDefId>::default(); // FIXME: Use `tcx.hir().par_body_owners()` when we implement creating `DefId`s // for anon constants during their parents' typeck. @@ -31,7 +36,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) { } let (path, _) = item.expect_use(); let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - format!("unused import: `{}`", snippet) + format!("unused import: `{snippet}`") } else { "unused import".to_owned() }; diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 3f65adcd36e..c930537d4ae 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -171,8 +171,7 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef create_err(&format!( "the trait `DispatchFromDyn` may only be implemented \ for a coercion between structures with the same \ - definition; expected `{}`, found `{}`", - source_path, target_path, + definition; expected `{source_path}`, found `{target_path}`", )) .emit(); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index f5326e50614..a94c75f918a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -148,8 +148,7 @@ impl<'tcx> InherentCollect<'tcx> { if let ty::Ref(_, subty, _) = ty.kind() { err.note(format!( "you could also try moving the reference to \ - uses of `{}` (such as `self`) within the implementation", - subty + uses of `{subty}` (such as `self`) within the implementation" )); } err.emit(); diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 3bd2931265c..7205b7a21a8 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -77,8 +77,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { "duplicate definitions with name `{}`", ident, ); - err.span_label(span, format!("duplicate definitions for `{}`", ident)); - err.span_label(*former, format!("other definition for `{}`", ident)); + err.span_label(span, format!("duplicate definitions for `{ident}`")); + err.span_label(*former, format!("other definition for `{ident}`")); err.emit(); } @@ -114,11 +114,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> { ); err.span_label( self.tcx.def_span(item1.def_id), - format!("duplicate definitions for `{}`", name), + format!("duplicate definitions for `{name}`"), ); err.span_label( self.tcx.def_span(item2.def_id), - format!("other definition for `{}`", name), + format!("other definition for `{name}`"), ); for cause in &overlap.intercrate_ambiguity_causes { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 05c78f57088..bbdb108c59b 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -352,7 +352,7 @@ fn emit_orphan_check_error<'tcx>( let this = |name: &str| { if !trait_ref.def_id.is_local() && !is_target_ty { - msg("this", &format!(" because this is a foreign trait")) + msg("this", " because this is a foreign trait") } else { msg("this", &format!(" because {name} are always foreign")) } @@ -412,9 +412,8 @@ fn emit_orphan_check_error<'tcx>( .span_label( sp, format!( - "type parameter `{}` must be covered by another type \ - when it appears before the first local type (`{}`)", - param_ty, local_type + "type parameter `{param_ty}` must be covered by another type \ + when it appears before the first local type (`{local_type}`)" ), ) .note( @@ -441,9 +440,8 @@ fn emit_orphan_check_error<'tcx>( .span_label( sp, format!( - "type parameter `{}` must be used as the type parameter for some \ + "type parameter `{param_ty}` must be used as the type parameter for some \ local type", - param_ty, ), ) .note( @@ -541,17 +539,16 @@ fn lint_auto_trait_impl<'tcx>( let self_descr = tcx.def_descr(self_type_did); match arg { ty::util::NotUniqueParam::DuplicateParam(arg) => { - lint.note(format!("`{}` is mentioned multiple times", arg)); + lint.note(format!("`{arg}` is mentioned multiple times")); } ty::util::NotUniqueParam::NotParam(arg) => { - lint.note(format!("`{}` is not a generic parameter", arg)); + lint.note(format!("`{arg}` is not a generic parameter")); } } lint.span_note( item_span, format!( - "try using the same sequence of generic parameters as the {} definition", - self_descr, + "try using the same sequence of generic parameters as the {self_descr} definition", ), ) }, @@ -568,10 +565,10 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for DisableAutoTraitVisitor<'tcx> { type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { let tcx = self.tcx; - if t != self.self_ty_root { - for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { + if ty != self.self_ty_root { + for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, ty) { match tcx.impl_polarity(impl_def_id) { ImplPolarity::Negative => return ControlFlow::Break(()), ImplPolarity::Reservation => {} @@ -584,7 +581,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: } } - match t.kind() { + match ty.kind() { ty::Adt(def, args) if def.is_phantom_data() => args.visit_with(self), ty::Adt(def, args) => { // @lcnr: This is the only place where cycles can happen. We avoid this @@ -599,7 +596,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: ControlFlow::Continue(()) } - _ => t.super_visit_with(self), + _ => ty.super_visit_with(self), } } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index c160cf2df6e..f568b751951 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -195,9 +195,9 @@ pub(crate) fn placeholder_type_error_diag<'tcx>( sugg.push((arg.span, (*type_name).to_string())); } else if let Some(span) = generics.span_for_param_suggestion() { // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`. - sugg.push((span, format!(", {}", type_name))); + sugg.push((span, format!(", {type_name}"))); } else { - sugg.push((generics.span, format!("<{}>", type_name))); + sugg.push((generics.span, format!("<{type_name}>"))); } } @@ -329,7 +329,7 @@ fn bad_placeholder<'tcx>( mut spans: Vec<Span>, kind: &'static str, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) }; + let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") }; spans.sort(); tcx.sess.create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind }) @@ -425,10 +425,8 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { | hir::ItemKind::Union(_, generics) => { let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics); let (lt_sp, sugg) = match generics.params { - [] => (generics.span, format!("<{}>", lt_name)), - [bound, ..] => { - (bound.span.shrink_to_lo(), format!("{}, ", lt_name)) - } + [] => (generics.span, format!("<{lt_name}>")), + [bound, ..] => (bound.span.shrink_to_lo(), format!("{lt_name}, ")), }; mpart_sugg = Some(errors::AssociatedTypeTraitUninferredGenericParamsMultipartSuggestion { fspan: lt_sp, @@ -1027,7 +1025,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { } else { tcx.sess.span_err( meta.span(), - format!("unknown meta item passed to `rustc_deny_explicit_impl` {:?}", meta), + format!("unknown meta item passed to `rustc_deny_explicit_impl` {meta:?}"), ); } } @@ -1505,7 +1503,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( .sess .source_map() .span_to_snippet(ast_ty.span) - .map_or_else(|_| String::new(), |s| format!(" `{}`", s)); + .map_or_else(|_| String::new(), |s| format!(" `{s}`")); tcx.sess.emit_err(errors::SIMDFFIHighlyExperimental { span: ast_ty.span, snip }); } }; diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index edcb9527fe2..6e1762c54f2 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -209,6 +209,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { | ItemKind::Struct(..) | ItemKind::OpaqueTy(..) | ItemKind::Union(..) => (None, Defaults::Allowed), + ItemKind::Const(..) => (None, Defaults::Deny), _ => (None, Defaults::FutureCompatDisallowed), } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 758e9dc7230..4b7743fae53 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -3,7 +3,7 @@ use crate::astconv::{AstConv, PredicateFilter}; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; @@ -113,7 +113,7 @@ pub(super) fn explicit_item_bounds( .. }) => associated_type_bounds(tcx, def_id, bounds, *span), hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }), + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: false, .. }), span, .. }) => { @@ -121,6 +121,27 @@ pub(super) fn explicit_item_bounds( let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) } + // Since RPITITs are astconv'd as projections in `ast_ty_to_ty`, when we're asking + // for the item bounds of the *opaques* in a trait's default method signature, we + // need to map these projections back to opaques. + hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait: true, origin, .. }), + span, + .. + }) => { + let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) + | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) = *origin + else { + bug!() + }; + let args = GenericArgs::identity_for_item(tcx, def_id); + let item_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); + tcx.arena.alloc_slice( + &opaque_type_bounds(tcx, def_id, bounds, item_ty, *span) + .to_vec() + .fold_with(&mut AssocTyToOpaque { tcx, fn_def_id: fn_def_id.to_def_id() }), + ) + } hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[], _ => bug!("item_bounds called on {:?}", def_id), }; @@ -135,3 +156,26 @@ pub(super) fn item_bounds( tcx.mk_clauses_from_iter(util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound))) }) } + +struct AssocTyToOpaque<'tcx> { + tcx: TyCtxt<'tcx>, + fn_def_id: DefId, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTyToOpaque<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(ty::Projection, projection_ty) = ty.kind() + && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) + = self.tcx.opt_rpitit_info(projection_ty.def_id) + && fn_def_id == self.fn_def_id + { + self.tcx.type_of(projection_ty.def_id).instantiate(self.tcx, projection_ty.args) + } else { + ty + } + } +} diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 1c9070600db..ab3b2dde078 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -66,7 +66,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local()); let opaque_ty_node = tcx.hir().get(opaque_ty_id); let Node::Item(&Item { - kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }), + kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping: Some(lifetime_mapping), .. }), .. }) = opaque_ty_node else { @@ -156,6 +156,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } ItemKind::Fn(.., generics, _) | ItemKind::TyAlias(_, generics) + | ItemKind::Const(_, generics, _) | ItemKind::Enum(_, generics) | ItemKind::Struct(_, generics) | ItemKind::Union(_, generics) => generics, @@ -762,6 +763,7 @@ pub(super) fn type_param_predicates( ItemKind::Fn(.., generics, _) | ItemKind::Impl(&hir::Impl { generics, .. }) | ItemKind::TyAlias(_, generics) + | ItemKind::Const(_, generics, _) | ItemKind::OpaqueTy(&OpaqueTy { generics, origin: hir::OpaqueTyOrigin::TyAlias { .. }, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 44f116ef2da..3cc6f574aec 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -137,12 +137,6 @@ enum Scope<'a> { s: ScopeRef<'a>, }, - /// A scope which either determines unspecified lifetimes or errors - /// on them (e.g., due to ambiguity). - Elision { - s: ScopeRef<'a>, - }, - /// Use a specific lifetime (if `Some`) or leave it unset (to be /// inferred in a function body or potentially error outside one), /// for the default choice of lifetime in a trait object type. @@ -211,7 +205,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { Scope::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(), Scope::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) @@ -325,9 +318,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { break (vec![], BinderScopeType::Normal); } - Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } - | Scope::AnonConstBoundary { s } => { + Scope::ObjectLifetimeDefault { s, .. } | Scope::AnonConstBoundary { s } => { scope = s; } @@ -526,16 +517,11 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { | hir::ItemKind::Macro(..) | hir::ItemKind::Mod(..) | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::Static(..) | hir::ItemKind::GlobalAsm(..) => { // These sorts of items have no lifetime parameters at all. intravisit::walk_item(self, item); } - hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => { - // No lifetime parameters, but implied 'static. - self.with(Scope::Elision { s: self.scope }, |this| { - intravisit::walk_item(this, item) - }); - } hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { .. }, .. @@ -596,6 +582,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }) } hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::Enum(_, generics) | hir::ItemKind::Struct(_, generics) | hir::ItemKind::Union(_, generics) @@ -603,21 +590,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { | hir::ItemKind::TraitAlias(generics, ..) | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => { // These kinds of items have only early-bound lifetime parameters. - let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); - self.record_late_bound_vars(item.hir_id(), vec![]); - let scope = Scope::Binder { - hir_id: item.hir_id(), - bound_vars, - scope_type: BinderScopeType::Normal, - s: self.scope, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - intravisit::walk_item(this, item); - }); - }); + self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item)); } } } @@ -727,12 +700,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // Elided lifetimes are not allowed in non-return // position impl Trait let scope = Scope::TraitRefBoundary { s: self.scope }; - self.with(scope, |this| { - let scope = Scope::Elision { s: this.scope }; - this.with(scope, |this| { - intravisit::walk_item(this, opaque_ty); - }) - }); + self.with(scope, |this| intravisit::walk_item(this, opaque_ty)); return; } @@ -795,39 +763,24 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { use self::hir::TraitItemKind::*; match trait_item.kind { Fn(_, _) => { - self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| { + self.visit_early_late(trait_item.hir_id(), trait_item.generics, |this| { intravisit::walk_trait_item(this, trait_item) }); } Type(bounds, ty) => { - let generics = &trait_item.generics; - let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); - self.record_late_bound_vars(trait_item.hir_id(), vec![]); - let scope = Scope::Binder { - hir_id: trait_item.hir_id(), - bound_vars, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - for bound in bounds { - this.visit_param_bound(bound); - } - if let Some(ty) = ty { - this.visit_ty(ty); - } - }) - }); - } - Const(_, _) => { - // Only methods and types support generics. - assert!(trait_item.generics.params.is_empty()); - intravisit::walk_trait_item(self, trait_item); + self.visit_early(trait_item.hir_id(), trait_item.generics, |this| { + this.visit_generics(&trait_item.generics); + for bound in bounds { + this.visit_param_bound(bound); + } + if let Some(ty) = ty { + this.visit_ty(ty); + } + }) } + Const(_, _) => self.visit_early(trait_item.hir_id(), trait_item.generics, |this| { + intravisit::walk_trait_item(this, trait_item) + }), } } @@ -835,34 +788,16 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { use self::hir::ImplItemKind::*; match impl_item.kind { - Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| { + Fn(..) => self.visit_early_late(impl_item.hir_id(), impl_item.generics, |this| { + intravisit::walk_impl_item(this, impl_item) + }), + Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { + this.visit_generics(impl_item.generics); + this.visit_ty(ty); + }), + Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { intravisit::walk_impl_item(this, impl_item) }), - Type(ty) => { - let generics = &impl_item.generics; - let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> = - generics.params.iter().map(ResolvedArg::early).collect(); - self.record_late_bound_vars(impl_item.hir_id(), vec![]); - let scope = Scope::Binder { - hir_id: impl_item.hir_id(), - bound_vars, - s: self.scope, - scope_type: BinderScopeType::Normal, - where_bound_origin: None, - }; - self.with(scope, |this| { - let scope = Scope::TraitRefBoundary { s: this.scope }; - this.with(scope, |this| { - this.visit_generics(generics); - this.visit_ty(ty); - }) - }); - } - Const(_, _) => { - // Only methods and types support generics. - assert!(impl_item.generics.params.is_empty()); - intravisit::walk_impl_item(self, impl_item); - } } } @@ -1198,6 +1133,25 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { self.with(scope, walk); } + fn visit_early<F>(&mut self, hir_id: hir::HirId, generics: &'tcx hir::Generics<'tcx>, walk: F) + where + F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>), + { + let bound_vars = generics.params.iter().map(ResolvedArg::early).collect(); + self.record_late_bound_vars(hir_id, vec![]); + let scope = Scope::Binder { + hir_id, + bound_vars, + s: self.scope, + scope_type: BinderScopeType::Normal, + where_bound_origin: None, + }; + self.with(scope, |this| { + let scope = Scope::TraitRefBoundary { s: this.scope }; + this.with(scope, walk) + }); + } + #[instrument(level = "debug", skip(self))] fn resolve_lifetime_ref( &mut self, @@ -1293,8 +1247,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { scope = s; } - Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } + Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } | Scope::AnonConstBoundary { s } => { @@ -1357,7 +1310,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Root { .. } => break, Scope::Binder { s, .. } | Scope::Body { s, .. } - | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } @@ -1409,8 +1361,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { scope = s; } - Scope::Elision { s, .. } - | Scope::ObjectLifetimeDefault { s, .. } + Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { scope = s; @@ -1483,7 +1434,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Root { .. } => break, Scope::Binder { s, .. } | Scope::Body { s, .. } - | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } @@ -1564,7 +1514,6 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { Scope::Body { .. } => break true, Scope::Binder { s, .. } - | Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } | Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } @@ -1832,14 +1781,20 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { output: Option<&'tcx hir::Ty<'tcx>>, in_closure: bool, ) { - self.with(Scope::Elision { s: self.scope }, |this| { - for input in inputs { - this.visit_ty(input); - } - if !in_closure && let Some(output) = output { - this.visit_ty(output); - } - }); + self.with( + Scope::ObjectLifetimeDefault { + lifetime: Some(ResolvedArg::StaticLifetime), + s: self.scope, + }, + |this| { + for input in inputs { + this.visit_ty(input); + } + if !in_closure && let Some(output) = output { + this.visit_ty(output); + } + }, + ); if in_closure && let Some(output) = output { self.visit_ty(output); } @@ -1859,7 +1814,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { scope = s; } - Scope::Root { .. } | Scope::Elision { .. } => break ResolvedArg::StaticLifetime, + Scope::Root { .. } => break ResolvedArg::StaticLifetime, Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, @@ -2057,8 +2012,7 @@ fn is_late_bound_map( tcx.sess.delay_span_bug( *span, format!( - "Incorrect generic arg count for alias {:?}", - alias_def + "Incorrect generic arg count for alias {alias_def:?}" ), ); None diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c39edaa1577..2bbdbe3a1f6 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -156,7 +156,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else { return Ty::new_error_with_message(tcx, tcx.def_span(def_id), - format!("unable to find type-dependent def for {:?}", parent_node_id), + format!("unable to find type-dependent def for {parent_node_id:?}"), ); }; let idx = segment @@ -197,14 +197,14 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { } else { return Ty::new_error_with_message(tcx, tcx.def_span(def_id), - format!("unable to find const parent for {} in pat {:?}", hir_id, pat), + format!("unable to find const parent for {hir_id} in pat {pat:?}"), ); } } _ => { return Ty::new_error_with_message(tcx, tcx.def_span(def_id), - format!("unexpected const parent path {:?}", parent_node), + format!("unexpected const parent path {parent_node:?}"), ); } }; @@ -404,7 +404,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty icx.to_ty(ty) } } - ItemKind::Const(ty, body_id) => { + ItemKind::Const(ty, _, body_id) => { if is_suggestable_infer_ty(ty) { infer_placeholder_type( tcx, def_id, body_id, ty.span, item.ident, "constant", @@ -544,7 +544,7 @@ fn infer_placeholder_type<'a>( if let Some(ty) = ty.make_suggestable(tcx, false) { err.span_suggestion( span, - format!("provide a type for the {item}", item = kind), + format!("provide a type for the {kind}"), format!("{colon} {ty}"), Applicability::MachineApplicable, ); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c2d2e5f7e50..0babdf7e5b3 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -216,7 +216,7 @@ impl<'a> IntoDiagnostic<'a> for MissingTypeParams { "parameters", self.missing_type_params .iter() - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .collect::<Vec<_>>() .join(", "), ); diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index f1765174d79..ca7679cfba0 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -130,7 +130,7 @@ fn diagnostic_hir_wf_check<'tcx>( hir::Node::Item(item) => match item.kind { hir::ItemKind::TyAlias(ty, _) | hir::ItemKind::Static(ty, _, _) - | hir::ItemKind::Const(ty, _) => vec![ty], + | hir::ItemKind::Const(ty, _, _) => vec![ty], hir::ItemKind::Impl(impl_) => match &impl_.of_trait { Some(t) => t .path diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 62f22bda9f9..4f705eaf10a 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -77,8 +77,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) tcx.sess.delay_span_bug( tcx.def_span(impl_def_id), format!( - "potentially unconstrained type parameters weren't evaluated: {:?}", - impl_self_ty, + "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}", ), ); return; @@ -180,7 +179,7 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol kind, name ); - err.span_label(span, format!("unconstrained {} parameter", kind)); + err.span_label(span, format!("unconstrained {kind} parameter")); if kind == "const" { err.note( "expressions using a const parameter must map each value to a distinct output value", diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 81993789bcf..8b2c93d8fd3 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -294,7 +294,7 @@ fn check_duplicate_params<'tcx>( if let (_, [duplicate, ..]) = base_params.partition_dedup() { let param = impl1_args[duplicate.0 as usize]; tcx.sess - .struct_span_err(span, format!("specializing impl repeats parameter `{}`", param)) + .struct_span_err(span, format!("specializing impl repeats parameter `{param}`")) .emit(); } } @@ -523,7 +523,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc } _ => { tcx.sess - .struct_span_err(span, format!("cannot specialize on predicate `{}`", predicate)) + .struct_span_err(span, format!("cannot specialize on predicate `{predicate}`")) .emit(); } } diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index d7e62457f36..4f95174f869 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -177,6 +177,7 @@ pub fn provide(providers: &mut Providers) { collect::provide(providers); coherence::provide(providers); check::provide(providers); + check_unused::provide(providers); variance::provide(providers); outlives::provide(providers); impl_wf_check::provide(providers); @@ -247,7 +248,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> { } }); - check_unused::check_crate(tcx); + tcx.ensure().check_unused_traits(()); if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) } } diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 0828fe9e0f2..6be8d72aed2 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -474,7 +474,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { verb ) } else { - format!("missing generics for {} `{}`", def_kind, def_path) + format!("missing generics for {def_kind} `{def_path}`") } } @@ -599,7 +599,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let span = self.path_segment.ident.span; // insert a suggestion of the form "Y<'a, 'b>" - let sugg = format!("<{}>", suggested_args); + let sugg = format!("<{suggested_args}>"); debug!("sugg: {:?}", sugg); err.span_suggestion_verbose( @@ -624,7 +624,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let sugg_suffix = if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" }; - let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix); + let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}"); debug!("sugg: {:?}", sugg); err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders); @@ -649,7 +649,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let span = self.path_segment.ident.span; // insert a suggestion of the form "Y<T, U>" - let sugg = format!("<{}>", suggested_args); + let sugg = format!("<{suggested_args}>"); debug!("sugg: {:?}", sugg); err.span_suggestion_verbose( @@ -682,7 +682,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let sugg_suffix = if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" }; - let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix); + let sugg = format!("{sugg_prefix}{suggested_args}{sugg_suffix}"); debug!("sugg: {:?}", sugg); err.span_suggestion_verbose(sugg_span, msg, sugg, Applicability::HasPlaceholders); @@ -1024,7 +1024,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .collect::<Vec<_>>() .join(", "); - format!(": {}", params) + format!(": {params}") }; format!( diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 3b286bb9c93..ed03c5da26f 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -32,8 +32,8 @@ pub enum VarianceTerm<'a> { impl<'a> fmt::Debug for VarianceTerm<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - ConstantTerm(c1) => write!(f, "{:?}", c1), - TransformTerm(v1, v2) => write!(f, "({:?} \u{00D7} {:?})", v1, v2), + ConstantTerm(c1) => write!(f, "{c1:?}"), + TransformTerm(v1, v2) => write!(f, "({v1:?} \u{00D7} {v2:?})"), InferredTerm(id) => write!(f, "[{}]", { let InferredIndex(i) = id; i diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a699cd6c942..2d8b956771b 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -420,12 +420,13 @@ impl<'a> State<'a> { fn print_associated_const( &mut self, ident: Ident, + generics: &hir::Generics<'_>, ty: &hir::Ty<'_>, default: Option<hir::BodyId>, ) { - self.head(""); self.word_space("const"); self.print_ident(ident); + self.print_generic_params(generics.params); self.word_space(":"); self.print_type(ty); if let Some(expr) = default { @@ -433,6 +434,7 @@ impl<'a> State<'a> { self.word_space("="); self.ann.nested(self, Nested::Body(expr)); } + self.print_where_clause(generics); self.word(";") } @@ -532,9 +534,10 @@ impl<'a> State<'a> { self.word(";"); self.end(); // end the outer cbox } - hir::ItemKind::Const(ty, expr) => { + hir::ItemKind::Const(ty, generics, expr) => { self.head("const"); self.print_ident(item.ident); + self.print_generic_params(generics.params); self.word_space(":"); self.print_type(ty); self.space(); @@ -542,6 +545,7 @@ impl<'a> State<'a> { self.word_space("="); self.ann.nested(self, Nested::Body(expr)); + self.print_where_clause(generics); self.word(";"); self.end(); // end the outer cbox } @@ -836,7 +840,7 @@ impl<'a> State<'a> { self.print_outer_attributes(self.attrs(ti.hir_id())); match ti.kind { hir::TraitItemKind::Const(ty, default) => { - self.print_associated_const(ti.ident, ty, default); + self.print_associated_const(ti.ident, ti.generics, ty, default); } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_names)) => { self.print_method_sig(ti.ident, sig, ti.generics, arg_names, None); @@ -865,7 +869,7 @@ impl<'a> State<'a> { match ii.kind { hir::ImplItemKind::Const(ty, expr) => { - self.print_associated_const(ii.ident, ty, Some(expr)); + self.print_associated_const(ii.ident, ii.generics, ty, Some(expr)); } hir::ImplItemKind::Fn(ref sig, body) => { self.head(""); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 3d012a15a67..2281343e250 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -77,6 +77,10 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters +hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expected_ty}` to `{$expr_ty}` +hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}` +hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}` + hir_typeck_return_stmt_outside_of_fn_body = {$statement_kind} statement outside of function body .encl_body_label = the {$statement_kind} is part of this body... diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 119ed2fa408..d5f03a6aaed 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -558,10 +558,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let pred = clause.kind().rebind(match clause.kind().skip_binder() { ty::ClauseKind::Trait(trait_pred) => { - // FIXME(rpitit): This will need to be fixed when we move to associated types assert!(matches!( *trait_pred.trait_ref.self_ty().kind(), - ty::Alias(_, ty::AliasTy { def_id, args: alias_args, .. }) + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. }) if def_id == rpit_def_id && args == alias_args )); ty::ClauseKind::Trait(trait_pred.with_self_ty(self.tcx, ty)) @@ -569,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ClauseKind::Projection(mut proj_pred) => { assert!(matches!( *proj_pred.projection_ty.self_ty().kind(), - ty::Alias(_, ty::AliasTy { def_id, args: alias_args, .. }) + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args: alias_args, .. }) if def_id == rpit_def_id && args == alias_args )); proj_pred = proj_pred.with_self_ty(self.tcx, ty); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index a24d1ff077f..c68f2d94f35 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -402,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .sess .struct_span_err( callee_expr.span, - format!("evaluate({:?}) = {:?}", predicate, result), + format!("evaluate({predicate:?}) = {result:?}"), ) .span_label(predicate_span, "predicate") .emit(); @@ -531,8 +531,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - let up_to_rcvr_span = segment.ident.span.until(callee_expr.span); - let rest_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); + let Some(callee_expr_span) = callee_expr.span.find_ancestor_inside(call_expr.span) + else { + return; + }; + let up_to_rcvr_span = segment.ident.span.until(callee_expr_span); + let rest_span = callee_expr_span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); let rest_snippet = if let Some(first) = rest.first() { self.tcx .sess @@ -581,6 +585,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_ty: Ty<'tcx>, arg_exprs: &'tcx [hir::Expr<'tcx>], ) -> ErrorGuaranteed { + // Callee probe fails when APIT references errors, so suppress those + // errors here. + if let Some((_, _, args)) = self.extract_callable_info(callee_ty) + && let Err(err) = args.error_reported() + { + return err; + } + let mut unit_variant = None; if let hir::ExprKind::Path(qpath) = &callee_expr.kind && let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _) diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7597ce88f93..5bc0e2ee86c 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let reported = self .tcx .sess - .delay_span_bug(span, format!("`{:?}` should be sized but is not?", t)); + .delay_span_bug(span, format!("`{t:?}` should be sized but is not?")); return Err(reported); } }) @@ -644,12 +644,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.span_suggestion( self.cast_span, "try casting to a reference instead", - format!("&{}{}", mtstr, s), + format!("&{mtstr}{s}"), Applicability::MachineApplicable, ); } Err(_) => { - let msg = format!("did you mean `&{}{}`?", mtstr, tstr); + let msg = format!("did you mean `&{mtstr}{tstr}`?"); err.span_help(self.cast_span, msg); } } @@ -705,10 +705,10 @@ impl<'a, 'tcx> CastCheck<'tcx> { ) }), |lint| { - lint.help(format!( + lint.help( "cast can be replaced by coercion; this might \ - require a temporary variable" - )) + require a temporary variable", + ) }, ); } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index affeee55e79..0624a4baf79 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -81,7 +81,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?bound_sig, ?liberated_sig); - let mut fcx = FnCtxt::new(self, self.param_env.without_const(), closure.def_id); + let mut fcx = FnCtxt::new(self, self.param_env, closure.def_id); let generator_types = check_fn( &mut fcx, liberated_sig, @@ -723,11 +723,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return None, - ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => self - .tcx - .explicit_item_bounds(proj.def_id) - .iter_instantiated_copied(self.tcx, proj.args) - .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, _ => span_bug!( self.tcx.def_span(expr_def_id), "async fn generator return type not an inference variable: {ret_ty}" diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3b1d9c8905b..4fdfc51bc86 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -46,6 +46,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult}; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::lint::in_external_macro; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, }; @@ -636,22 +637,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) if traits.contains(&trait_pred.def_id()) => { - let trait_pred = self.resolve_vars_if_possible(trait_pred); - if unsize_did == trait_pred.def_id() { - let self_ty = trait_pred.self_ty(); - let unsize_ty = trait_pred.trait_ref.args[1].expect_ty(); - if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) = - (self_ty.kind(), unsize_ty.kind()) - && data_a.principal_def_id() != data_b.principal_def_id() - { - debug!("coerce_unsized: found trait upcasting coercion"); - has_trait_upcasting_coercion = Some((self_ty, unsize_ty)); - } - if let ty::Tuple(..) = unsize_ty.kind() { - debug!("coerce_unsized: found unsized tuple coercion"); - has_unsized_tuple_coercion = true; - } - } trait_pred } _ => { @@ -659,6 +644,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { continue; } }; + let trait_pred = self.resolve_vars_if_possible(trait_pred); match selcx.select(&obligation.with(selcx.tcx(), trait_pred)) { // Uncertain or unimplemented. Ok(None) => { @@ -701,20 +687,28 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // be silent, as it causes a type mismatch later. } - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), + Ok(Some(impl_source)) => { + // Some builtin coercions are still unstable so we detect + // these here and emit a feature error if coercion doesn't fail + // due to another reason. + match impl_source { + traits::ImplSource::Builtin( + BuiltinImplSource::TraitUpcasting { .. }, + _, + ) => { + has_trait_upcasting_coercion = + Some((trait_pred.self_ty(), trait_pred.trait_ref.args.type_at(1))); + } + traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { + has_unsized_tuple_coercion = true; + } + _ => {} + } + queue.extend(impl_source.nested_obligations()) + } } } - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { - feature_err( - &self.tcx.sess.parse_sess, - sym::unsized_tuple_coercion, - self.cause.span, - "unsized tuple coercion is not stable enough for use and is subject to change", - ) - .emit(); - } - if let Some((sub, sup)) = has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting { @@ -730,6 +724,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { err.emit(); } + if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { + feature_err( + &self.tcx.sess.parse_sess, + sym::unsized_tuple_coercion, + self.cause.span, + "unsized tuple coercion is not stable enough for use and is subject to change", + ) + .emit(); + } + Ok(coercion) } @@ -1793,8 +1797,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_note( sp, format!( - "return type inferred to be `{}` here", - expected + "return type inferred to be `{expected}` here" ), ); } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 429c3366fed..26fa3d80d55 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_no_capture_closure(err, expected, expr_ty) || self.suggest_boxing_when_appropriate(err, expr.span, expr.hir_id, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) - || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) + || self.suggest_copied_cloned_or_as_ref(err, expr, expr_ty, expected, expected_ty_expr) || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected) @@ -621,7 +621,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // is in a different line, so we point at both. err.span_label(secondary_span, "expected due to the type of this binding"); err.span_label(primary_span, format!("expected due to this{post_message}")); - } else if post_message == "" { + } else if post_message.is_empty() { // We are pointing at either the assignment lhs or the binding def pattern. err.span_label(primary_span, "expected due to the type of this binding"); } else { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 05906a4b9f5..36096aa35d4 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -252,6 +252,45 @@ impl HelpUseLatestEdition { } } +#[derive(Subdiagnostic)] +pub enum OptionResultRefMismatch<'tcx> { + #[suggestion( + hir_typeck_option_result_copied, + code = ".copied()", + style = "verbose", + applicability = "machine-applicable" + )] + Copied { + #[primary_span] + span: Span, + def_path: String, + }, + #[suggestion( + hir_typeck_option_result_cloned, + code = ".cloned()", + style = "verbose", + applicability = "machine-applicable" + )] + Cloned { + #[primary_span] + span: Span, + def_path: String, + }, + #[suggestion( + hir_typeck_option_result_asref, + code = ".as_ref()", + style = "verbose", + applicability = "machine-applicable" + )] + AsRef { + #[primary_span] + span: Span, + def_path: String, + expected_ty: Ty<'tcx>, + expr_ty: Ty<'tcx>, + }, +} + #[derive(Diagnostic)] #[diag(hir_typeck_const_select_must_be_const)] #[help] diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 29488c9011a..8f5737dd4ad 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1334,7 +1334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { t_cast, t.span, expr.span, - self.param_env.constness(), + hir::Constness::NotConst, ) { Ok(cast_check) => { debug!( @@ -1394,7 +1394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(( _, hir::Node::Local(hir::Local { ty: Some(ty), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. }), + | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }), )) = parent_node else { return; @@ -1428,7 +1428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a new function context. let def_id = block.def_id; - let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id); + let fcx = FnCtxt::new(self, self.param_env, def_id); crate::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(&body.value, expected); @@ -1890,7 +1890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut truncated_fields_error = String::new(); let remaining_fields_names = match &displayable_field_names[..] { - [field1] => format!("`{}`", field1), + [field1] => format!("`{field1}`"), [field1, field2] => format!("`{field1}` and `{field2}`"), [field1, field2, field3] => format!("`{field1}`, `{field2}` and `{field3}`"), _ => { @@ -2117,16 +2117,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } _ => { - err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty)); + err.span_label(variant_ident_span, format!("`{ty}` defined here")); err.span_label(field.ident.span, "field does not exist"); err.span_suggestion_verbose( expr_span, - format!( - "`{adt}` is a tuple {kind_name}, use the appropriate syntax", - adt = ty, - kind_name = kind_name, - ), - format!("{adt}(/* fields */)", adt = ty), + format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",), + format!("{ty}(/* fields */)"), Applicability::HasPlaceholders, ); } @@ -2243,7 +2239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // dynamic limit, to never omit just one field let limit = if names.len() == 6 { 6 } else { 5 }; let mut display = - names.iter().take(limit).map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", "); + names.iter().take(limit).map(|n| format!("`{n}`")).collect::<Vec<_>>().join(", "); if names.len() > limit { display = format!("{} ... and {} others", display, names.len() - limit); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index bd7e7a671e6..ecafb50f420 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - let msg = format!("unreachable {}", kind); + let msg = format!("unreachable {kind}"); self.tcx().struct_span_lint_hir( lint::builtin::UNREACHABLE_CODE, id, @@ -134,7 +134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn tag(&self) -> String { - format!("{:p}", self) + format!("{self:p}") } pub fn local_ty(&self, span: Span, nid: hir::HirId) -> Ty<'tcx> { @@ -860,7 +860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) .and_then(|r| { // lint bare trait if the method is found in the trait - if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { + if span.edition().at_least_rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { diag.emit(); } Ok(r) @@ -890,7 +890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // emit or cancel the diagnostic for bare traits - if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { + if span.edition().at_least_rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) { if trait_missing_method { // cancel the diag for bare traits when meeting `MyTrait::missing_method` diag.cancel(); @@ -908,7 +908,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error, None, Expectation::NoExpectation, - trait_missing_method && span.edition().rust_2021(), // emits missing method for trait only after edition 2021 + trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 ) { e.emit(); } @@ -1069,7 +1069,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if let ExprKind::MethodCall(..) = rcvr.kind { err.span_note( sp, - modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.", + modifies_rcvr_note + ", it is not meant to be used in method chains.", ); } else { err.span_note(sp, modifies_rcvr_note); @@ -1412,9 +1412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.sess.delay_span_bug( span, format!( - "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", - self_ty, - impl_ty, + "instantiate_value_path: (UFCS) {self_ty:?} was a subtype of {impl_ty:?} but now is not?", ), ); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a9610009db1..e102c51c7ee 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -45,12 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len()); for cast in deferred_cast_checks.drain(..) { - let prev_env = self.param_env; - self.param_env = self.param_env.with_constness(cast.constness); - cast.check(self); - - self.param_env = prev_env; } *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks; @@ -689,7 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label( full_call_span, - format!("arguments to this {} are incorrect", call_name), + format!("arguments to this {call_name} are incorrect"), ); } else { err = tcx.sess.struct_span_err_with_code( @@ -796,10 +791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, None, ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); + err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind && provided_idx.as_usize() == expected_idx.as_usize() @@ -874,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty.is_unit() { "()".to_string() } else if ty.is_suggestable(tcx, false) { - format!("/* {} */", ty) + format!("/* {ty} */") } else if let Some(fn_def_id) = fn_def_id && self.tcx.def_kind(fn_def_id).is_fn_like() && let self_implicit = @@ -931,12 +923,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; let provided_ty_name = if !has_error_or_infer([provided_ty]) { // FIXME: not suggestable, use something else - format!(" of type `{}`", provided_ty) + format!(" of type `{provided_ty}`") } else { "".to_string() }; - labels - .push((provided_span, format!("unexpected argument{}", provided_ty_name))); + labels.push((provided_span, format!("unexpected argument{provided_ty_name}"))); let mut span = provided_span; if span.can_be_used_for_suggestions() { if arg_idx.index() > 0 @@ -1009,11 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args_span }; let rendered = if !has_error_or_infer([input_ty]) { - format!(" of type `{}`", input_ty) + format!(" of type `{input_ty}`") } else { "".to_string() }; - labels.push((span, format!("an argument{} is missing", rendered))); + labels.push((span, format!("an argument{rendered} is missing"))); suggestion_text = match suggestion_text { SuggestionText::None => SuggestionText::Provide(false), SuggestionText::Provide(_) => SuggestionText::Provide(true), @@ -1034,13 +1025,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rendered = if !has_error_or_infer([first_expected_ty, second_expected_ty]) { format!( - " of type `{}` and `{}`", - first_expected_ty, second_expected_ty + " of type `{first_expected_ty}` and `{second_expected_ty}`" ) } else { "".to_string() }; - labels.push((span, format!("two arguments{} are missing", rendered))); + labels.push((span, format!("two arguments{rendered} are missing"))); suggestion_text = match suggestion_text { SuggestionText::None | SuggestionText::Provide(_) => { SuggestionText::Provide(true) @@ -1066,13 +1056,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { third_expected_ty, ]) { format!( - " of type `{}`, `{}`, and `{}`", - first_expected_ty, second_expected_ty, third_expected_ty + " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" ) } else { "".to_string() }; - labels.push((span, format!("three arguments{} are missing", rendered))); + labels.push((span, format!("three arguments{rendered} are missing"))); suggestion_text = match suggestion_text { SuggestionText::None | SuggestionText::Provide(_) => { SuggestionText::Provide(true) @@ -1113,25 +1102,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { - format!(", found `{}`", first_provided_ty) + format!(", found `{first_provided_ty}`") } else { String::new() }; labels.push(( first_span, - format!("expected `{}`{}", first_expected_ty, first_provided_ty_name), + format!("expected `{first_expected_ty}`{first_provided_ty_name}"), )); let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { - format!(", found `{}`", second_provided_ty) + format!(", found `{second_provided_ty}`") } else { String::new() }; labels.push(( second_span, - format!("expected `{}`{}", second_expected_ty, second_provided_ty_name), + format!("expected `{second_expected_ty}`{second_provided_ty_name}"), )); suggestion_text = match suggestion_text { @@ -1144,13 +1133,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; let (provided_ty, provided_span) = provided_arg_tys[dest_input]; let provided_ty_name = if !has_error_or_infer([provided_ty]) { - format!(", found `{}`", provided_ty) + format!(", found `{provided_ty}`") } else { String::new() }; labels.push(( provided_span, - format!("expected `{}`{}", expected_ty, provided_ty_name), + format!("expected `{expected_ty}`{provided_ty_name}"), )); } @@ -2031,7 +2020,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ("closure", self.tcx.def_span(def_id)) }; - err.span_note(span, format!("{} defined here", kind)); + err.span_note(span, format!("{kind} defined here")); } else { err.span_note( self.tcx.def_span(def_id), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ec19d017c25..c49e4381897 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,8 +1,6 @@ use super::FnCtxt; -use crate::errors::{ - AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod, -}; +use crate::errors; use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; @@ -397,7 +395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let struct_pat_shorthand_field = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr); if let Some(name) = struct_pat_shorthand_field { - sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name))); + sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: "))); } Some(sugg) }) @@ -434,7 +432,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`, // but those checks need to be a bit more delicate and the benefit is diminishing. if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref { - err.subdiagnostic(SuggestConvertViaMethod { + err.subdiagnostic(errors::SuggestConvertViaMethod { span: expr.span.shrink_to_hi(), sugg: ".as_ref()", expected, @@ -447,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && self.can_eq(self.param_env, deref_ty, peeled) && error_tys_equate_as_ref { - err.subdiagnostic(SuggestConvertViaMethod { + err.subdiagnostic(errors::SuggestConvertViaMethod { span: expr.span.shrink_to_hi(), sugg: ".as_deref()", expected, @@ -521,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.can_coerce(Ty::new_box(self.tcx, found), expected) { let suggest_boxing = match found.kind() { ty::Tuple(tuple) if tuple.is_empty() => { - SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span } + errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span } } ty::Generator(def_id, ..) if matches!( @@ -529,9 +527,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) ) => { - SuggestBoxing::AsyncBody + errors::SuggestBoxing::AsyncBody } - _ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() }, + _ => errors::SuggestBoxing::Other { + start: span.shrink_to_lo(), + end: span.shrink_to_hi(), + }, }; err.subdiagnostic(suggest_boxing); @@ -558,7 +559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .take(4) .map(|(var_hir_id, upvar)| { let var_name = self.tcx.hir().name(*var_hir_id).to_string(); - let msg = format!("`{}` captured here", var_name); + let msg = format!("`{var_name}` captured here"); (upvar.span, msg) }) .collect::<Vec<_>>(); @@ -756,23 +757,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &fn_decl.output { &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { // `fn main()` must return `()`, do not suggest changing return type - err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); + err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span }); return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { if let Some(found) = found.make_suggestable(self.tcx, false) { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); + err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }); return true; } else if let ty::Closure(_, args) = found.kind() // FIXME(compiler-errors): Get better at printing binders... && let closure = args.as_closure() && closure.sig().is_suggestable(self.tcx, false) { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); + err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); return true; } else { // FIXME: if `found` could be `impl Iterator` we should suggest that. - err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); + err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span }); return true } } @@ -794,10 +795,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!(?found); if found.is_suggestable(self.tcx, false) { if term.span.is_empty() { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); + err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() }); return true; } else { - err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); + err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected }); } } } @@ -813,7 +814,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.normalize(span, ty); let ty = self.tcx.erase_late_bound_regions(ty); if self.can_coerce(expected, ty) { - err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); + err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other { span, expected }); self.try_suggest_return_impl_trait(err, expected, ty, fn_id); return true; } @@ -931,7 +932,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion( fn_return.span(), "consider using an impl return type", - format!("impl {}", all_bounds_str), + format!("impl {all_bounds_str}"), Applicability::MaybeIncorrect, ); } @@ -1070,7 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .must_apply_modulo_regions() { let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!(": {}.clone()", ident), + Some(ident) => format!(": {ident}.clone()"), None => ".clone()".to_string() }; @@ -1085,12 +1086,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } - pub(crate) fn suggest_copied_or_cloned( + pub(crate) fn suggest_copied_cloned_or_as_ref( &self, diag: &mut Diagnostic, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let ty::Adt(adt_def, args) = expr_ty.kind() else { return false; @@ -1102,55 +1104,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - let mut suggest_copied_or_cloned = || { + if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result) + && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1)) + || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option) + { let expr_inner_ty = args.type_at(0); let expected_inner_ty = expected_args.type_at(0); - if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() - && self.can_eq(self.param_env, ty, expected_inner_ty) - { - let def_path = self.tcx.def_path_str(adt_def.did()); - if self.type_is_copy_modulo_regions(self.param_env, ty) { - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!( - "use `{def_path}::copied` to copy the value inside the `{def_path}`" - ), - ".copied()", - Applicability::MachineApplicable, - ); - return true; - } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() - && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( - self, - self.param_env, - ty, - clone_did, - ) + if let &ty::Ref(_, ty, mutability) = expr_inner_ty.kind() + && self.can_eq(self.param_env, ty, expected_inner_ty) { - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!( - "use `{def_path}::cloned` to clone the value inside the `{def_path}`" - ), - ".cloned()", - Applicability::MachineApplicable, - ); + let def_path = self.tcx.def_path_str(adt_def.did()); + let span = expr.span.shrink_to_hi(); + let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) { + errors::OptionResultRefMismatch::Copied { + span, def_path + } + } else if let Some(expected_ty_expr) = expected_ty_expr + // FIXME: suggest changes to both expressions to convert both to + // Option/Result<&T> + && mutability.is_not() + { + errors::OptionResultRefMismatch::AsRef { + span: expected_ty_expr.span.shrink_to_hi(), expected_ty, expr_ty, def_path + } + } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() + && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( + self, + self.param_env, + ty, + clone_did, + ) + { + errors::OptionResultRefMismatch::Cloned { + span, def_path + } + } else { + return false; + }; + diag.subdiagnostic(subdiag); return true; } - } - false - }; - - if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) - && adt_def.did() == result_did - // Check that the error types are equal - && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1)) - { - return suggest_copied_or_cloned(); - } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) - && adt_def.did() == option_did - { - return suggest_copied_or_cloned(); } false @@ -1248,7 +1241,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!(": {}.is_some()", ident), + Some(ident) => format!(": {ident}.is_some()"), None => ".is_some()".to_string(), }; @@ -1512,9 +1505,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found_ty: Ty<'tcx>, expr: &hir::Expr<'_>, ) { + // When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get + // `foo` and `clone`. + let expr = self.note_type_is_not_clone_inner_expr(expr); + + // If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`. let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; + let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; @@ -1567,6 +1566,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Given a type mismatch error caused by `&T` being cloned instead of `T`, and + /// the `expr` as the source of this type mismatch, try to find the method call + /// as the source of this error and return that instead. Otherwise, return the + /// original expression. + fn note_type_is_not_clone_inner_expr<'b>( + &'b self, + expr: &'b hir::Expr<'b>, + ) -> &'b hir::Expr<'b> { + match expr.peel_blocks().kind { + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { segments: [_], res: crate::Res::Local(binding), .. }, + )) => { + let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding) + else { + return expr; + }; + let Some(parent) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) else { + return expr; + }; + + match parent { + // foo.clone() + hir::Node::Local(hir::Local { init: Some(init), .. }) => { + self.note_type_is_not_clone_inner_expr(init) + } + // When `expr` is more complex like a tuple + hir::Node::Pat(hir::Pat { + hir_id: pat_hir_id, + kind: hir::PatKind::Tuple(pats, ..), + .. + }) => { + let Some(hir::Node::Local(hir::Local { init: Some(init), .. })) = + self.tcx.hir().find(self.tcx.hir().parent_id(*pat_hir_id)) else { + return expr; + }; + + match init.peel_blocks().kind { + ExprKind::Tup(init_tup) => { + if let Some(init) = pats + .iter() + .enumerate() + .filter(|x| x.1.hir_id == *hir_id) + .map(|(i, _)| init_tup.get(i).unwrap()) + .next() + { + self.note_type_is_not_clone_inner_expr(init) + } else { + expr + } + } + _ => expr, + } + } + _ => expr, + } + } + // If we're calling into a closure that may not be typed recurse into that call. no need + // to worry if it's a call to a typed function or closure as this would ne handled + // previously. + hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) = call_expr_kind + && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path + && let Some(hir::Node::Pat(hir::Pat { hir_id, .. })) = self.tcx.hir().find(*binding) + && let Some(closure) = self.tcx.hir().find(self.tcx.hir().parent_id(*hir_id)) + && let hir::Node::Local(hir::Local { init: Some(init), .. }) = closure + && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), ..} = init + { + let hir::Body { value: body_expr, .. } = self.tcx.hir().body(*body_id); + self.note_type_is_not_clone_inner_expr(body_expr) + } else { + expr + } + } + _ => expr, + } + } + /// A common error is to add an extra semicolon: /// /// ```compile_fail,E0308 diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index b059db23bb4..cfedcee9956 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -443,9 +443,9 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { // We add an edge to the hir_id of the expression/block we are breaking out of, and // then in process_deferred_edges we will map this hir_id to its PostOrderId, which // will refer to the end of the block due to the post order traversal. - self.find_target_expression_from_destination(destination).map_or((), |target| { + if let Ok(target) = self.find_target_expression_from_destination(destination) { self.drop_ranges.add_control_edge_hir_id(self.expr_index, target) - }); + } if let Some(value) = value { self.visit_expr(value); diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs index e01dcf83a38..e563bd40b65 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs @@ -125,8 +125,8 @@ impl Debug for TrackedValue { write!(f, "{}", tcx.hir().node_to_string(self.hir_id())) } else { match self { - Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id), - Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id), + Self::Variable(hir_id) => write!(f, "Variable({hir_id:?})"), + Self::Temporary(hir_id) => write!(f, "Temporary({hir_id:?})"), } } }) diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index 50a3bbf49d5..29413f08012 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -150,9 +150,10 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { hir.node_to_string(diag_expr_id), hir.node_to_string(parent) ); - place_with_id - .try_into() - .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value)); + + if let Ok(tracked_value) = place_with_id.try_into() { + self.mark_consumed(parent, tracked_value) + } } fn borrow( diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index 86ea092bc40..6a817122491 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -112,7 +112,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { self.fcx .tcx .sess - .delay_span_bug(span, format!("Encountered var {:?}", unresolved_term)); + .delay_span_bug(span, format!("Encountered var {unresolved_term:?}")); } else { let note = format!( "the type is part of the {} because of this {}", diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index 2d85451c9e1..4e65182f158 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), Ok(SizeSkeleton::Known(size)) => { if let Some(v) = u128::from(size.bytes()).checked_mul(8) { - format!("{} bits", v) + format!("{v} bits") } else { // `u128` should definitely be able to hold the size of different architectures // larger sizes should be reported as error `are too big for the current architecture` @@ -122,14 +122,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - let mut should_delay_as_bug = false; - if let Err(LayoutError::Unknown(bad_from)) = sk_from && bad_from.references_error() { - should_delay_as_bug = true; - } - if let Err(LayoutError::Unknown(bad_to)) = sk_to && bad_to.references_error() { - should_delay_as_bug = true; - } - if should_delay_as_bug { + if let Err(LayoutError::ReferencesError(_)) = sk_from { + err.delay_as_bug(); + } else if let Err(LayoutError::ReferencesError(_)) = sk_to { err.delay_as_bug(); } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 28e7f290737..c4d3cbc9faa 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -71,7 +71,7 @@ use rustc_middle::traits; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{sym, Span}; +use rustc_span::Span; fluent_messages! { "../messages.ftl" } @@ -101,7 +101,7 @@ fn primary_body_of( ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { match node { Node::Item(item) => match item.kind { - hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { + hir::ItemKind::Const(ty, _, body) | hir::ItemKind::Static(ty, _, body) => { Some((body, Some(ty), None)) } hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), @@ -182,11 +182,7 @@ fn typeck_with_fallback<'tcx>( let body = tcx.hir().body(body_id); let param_env = tcx.param_env(def_id); - let param_env = if tcx.has_attr(def_id, sym::rustc_do_not_const_check) { - param_env.without_const() - } else { - param_env - }; + let inh = Inherited::new(tcx, def_id); let mut fcx = FnCtxt::new(&inh, param_env, def_id); @@ -263,11 +259,7 @@ fn typeck_with_fallback<'tcx>( // Closure and generator analysis may run after fallback // because they don't constrain other type variables. - // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now) - let prev_constness = fcx.param_env.constness(); - fcx.param_env = fcx.param_env.without_const(); fcx.closure_analyze(body); - fcx.param_env = fcx.param_env.with_constness(prev_constness); assert!(fcx.deferred_call_resolutions.borrow().is_empty()); // Before the generator analysis, temporary scopes shall be marked to provide more // precise information on types to be captured. diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 6835782b5bd..7c73f6a89cd 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -225,7 +225,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { assert!(mutbl.is_mut()); Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) } - other => panic!("Cannot adjust receiver type {:?} to const ptr", other), + other => panic!("Cannot adjust receiver type {other:?} to const ptr"), }; adjustments.push(Adjustment { @@ -262,8 +262,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let impl_def_id = pick.item.container_id(self.tcx); assert!( self.tcx.impl_trait_ref(impl_def_id).is_none(), - "impl {:?} is not an inherent impl", - impl_def_id + "impl {impl_def_id:?} is not an inherent impl" ); self.fresh_args_for_item(self.span, impl_def_id) } diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index ec4e7f7f88a..5b19a4c525f 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -32,7 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Rust 2021 and later is already using the new prelude - if span.rust_2021() { + if span.at_least_rust_2021() { return; } @@ -97,28 +97,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = pick.autoref_or_ptr_adjustment { - format!("{}{} as *const _", derefs, self_expr) + format!("{derefs}{self_expr} as *const _") } else { - format!("{}{}{}", autoref, derefs, self_expr) + format!("{autoref}{derefs}{self_expr}") }; lint.span_suggestion( sp, "disambiguate the method call", - format!("({})", self_adjusted), + format!("({self_adjusted})"), Applicability::MachineApplicable, ); } else { let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = pick.autoref_or_ptr_adjustment { - format!("{}(...) as *const _", derefs) + format!("{derefs}(...) as *const _") } else { - format!("{}{}...", autoref, derefs) + format!("{autoref}{derefs}...") }; lint.span_help( sp, - format!("disambiguate the method call with `({})`", self_adjusted,), + format!("disambiguate the method call with `({self_adjusted})`",), ); } @@ -168,7 +168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .ok()) { // Keep turbofish. - format!("::{}", args) + format!("::{args}") } else { String::new() }, @@ -203,7 +203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pick: &Pick<'tcx>, ) { // Rust 2021 and later is already using the new prelude - if span.rust_2021() { + if span.at_least_rust_2021() { return; } @@ -347,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Glob import, so just use its name. return None; } else { - return Some(format!("{}", any_id)); + return Some(format!("{any_id}")); } } @@ -396,9 +396,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = pick.autoref_or_ptr_adjustment { - format!("{}{} as *const _", derefs, expr_text) + format!("{derefs}{expr_text} as *const _") } else { - format!("{}{}{}", autoref, derefs, expr_text) + format!("{autoref}{derefs}{expr_text}") }; (adjusted_text, precise) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9ef97243c5e..05eed1923d1 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -437,7 +437,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.rust_2018() { + if self.tcx.sess.at_least_rust_2018() { self.tcx.sess.emit_err(MethodCallOnUnknownRawPointee { span }); } else { self.tcx.struct_span_lint_hir( @@ -1592,7 +1592,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if let Some(method_name) = self.method_name { // Some trait methods are excluded for arrays before 2021. // (`array.into_iter()` wants a slice iterator for compatibility.) - if self_ty.is_array() && !method_name.span.rust_2021() { + if self_ty.is_array() && !method_name.span.at_least_rust_2021() { let trait_def = self.tcx.trait_def(trait_ref.def_id); if trait_def.skip_array_during_method_dispatch { return ProbeResult::NoMatch; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 3d7187cb16f..f6c07931023 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -153,7 +153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { E0034, "multiple applicable items in scope" ); - err.span_label(item_name.span, format!("multiple `{}` found", item_name)); + err.span_label(item_name.span, format!("multiple `{item_name}` found")); self.note_candidates_on_method_error( rcvr_ty, @@ -177,13 +177,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind, item_name ); - err.span_label(item_name.span, format!("private {}", kind)); + err.span_label(item_name.span, format!("private {kind}")); let sp = self .tcx .hir() .span_if_local(def_id) .unwrap_or_else(|| self.tcx.def_span(def_id)); - err.span_label(sp, format!("private {} defined here", kind)); + err.span_label(sp, format!("private {kind} defined here")); self.suggest_valid_traits(&mut err, out_of_scope_traits); err.emit(); } @@ -218,7 +218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { *region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, ); - let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty); + let msg = format!("you need `{trait_type}` instead of `{rcvr_ty}`"); let mut kind = &self_expr.kind; while let hir::ExprKind::AddrOf(_, _, expr) | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind @@ -637,7 +637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Point at the closure that couldn't satisfy the bound. ty::Closure(def_id, _) => bound_spans - .push((tcx.def_span(*def_id), format!("doesn't satisfy `{}`", quiet))), + .push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`"))), _ => {} } }; @@ -659,7 +659,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let term = pred.skip_binder().term; - let obligation = format!("{} = {}", projection_ty, term); + let obligation = format!("{projection_ty} = {term}"); let quiet = with_forced_trimmed_paths!(format!( "{} = {}", quiet_projection_ty, term @@ -672,7 +672,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let p = poly_trait_ref.trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); - let obligation = format!("{}: {}", self_ty, path); + let obligation = format!("{self_ty}: {path}"); let quiet = with_forced_trimmed_paths!(format!("_: {}", path)); bound_span_label(self_ty, &obligation, &quiet); Some((obligation, self_ty)) @@ -825,12 +825,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut preds: Vec<_> = predicates .iter() .filter_map(|pred| format_pred(**pred)) - .map(|(p, _)| format!("`{}`", p)) + .map(|(p, _)| format!("`{p}`")) .collect(); preds.sort(); preds.dedup(); let msg = if let [pred] = &preds[..] { - format!("trait bound {} was not satisfied", pred) + format!("trait bound {pred} was not satisfied") } else { format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) }; @@ -875,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggested_bounds.insert(pred); } } - format!("`{}`\nwhich is required by `{}`", p, parent_p) + format!("`{p}`\nwhich is required by `{parent_p}`") } }, }, @@ -1034,8 +1034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "".to_string() }; err.note(format!( - "the {item_kind} was found for\n{}{}", - type_candidates, additional_types + "the {item_kind} was found for\n{type_candidates}{additional_types}" )); } else { 'outer: for inherent_impl_did in self.tcx.inherent_impls(adt.did()) { @@ -1249,8 +1248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ( format!( - "the candidate is defined in an impl{} for the type `{}`", - insertion, impl_ty, + "the candidate is defined in an impl{insertion} for the type `{impl_ty}`", ), None, ) @@ -1452,11 +1450,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion( sugg_span, "use associated function syntax instead", - format!("{}::{}{}", ty_str, item_name, args), + format!("{ty_str}::{item_name}{args}"), applicability, ); } else { - err.help(format!("try with `{}::{}`", ty_str, item_name,)); + err.help(format!("try with `{ty_str}::{item_name}`",)); } } @@ -1491,9 +1489,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_span = expr.span.to(item_name.span); err.multipart_suggestion( format!( - "to call the function stored in `{}`, \ + "to call the function stored in `{item_name}`, \ surround the field access with parentheses", - item_name, ), vec![ (expr_span.shrink_to_lo(), '('.to_string()), @@ -1516,7 +1513,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let field_kind = if is_accessible { "field" } else { "private field" }; - err.span_label(item_name.span, format!("{}, not a method", field_kind)); + err.span_label(item_name.span, format!("{field_kind}, not a method")); return true; } false @@ -1669,8 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lit.span, format!( "you must specify a concrete type for this numeric value, \ - like `{}`", - concrete_type + like `{concrete_type}`" ), format!("{snippet}_{concrete_type}"), Applicability::MaybeIncorrect, @@ -1685,8 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let parent_node = self.tcx.hir().get_parent(hir_id); let msg = format!( - "you must specify a type for this binding, like `{}`", - concrete_type, + "you must specify a type for this binding, like `{concrete_type}`", ); match (filename, parent_node) { @@ -2194,7 +2189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() { if last_self_name == &self_name { - last_trait_names.push_str(format!(", {}", trait_name).as_str()); + last_trait_names.push_str(format!(", {trait_name}").as_str()); continue; } } @@ -2226,8 +2221,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (self_name, self_span, traits) in &derives_grouped { err.span_suggestion_verbose( self_span.shrink_to_lo(), - format!("consider annotating `{}` with `#[derive({})]`", self_name, traits), - format!("#[derive({})]\n", traits), + format!("consider annotating `{self_name}` with `#[derive({traits})]`"), + format!("#[derive({traits})]\n"), Applicability::MaybeIncorrect, ); } @@ -2475,7 +2470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if pick.autoderefs == 0 && !skip { err.span_label( pick.item.ident(self.tcx).span, - format!("the method is available for `{}` here", rcvr_ty), + format!("the method is available for `{rcvr_ty}` here"), ); } break; @@ -2521,13 +2516,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if pick.autoderefs == 0 && !skip { err.span_label( pick.item.ident(self.tcx).span, - format!("the method is available for `{}` here", new_rcvr_t), + format!("the method is available for `{new_rcvr_t}` here"), ); err.multipart_suggestion( "consider wrapping the receiver expression with the \ appropriate type", vec![ - (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)), + (rcvr.span.shrink_to_lo(), format!("{pre}({post}")), (rcvr.span.shrink_to_hi(), ")".to_string()), ], Applicability::MaybeIncorrect, @@ -2767,7 +2762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_suggestions( sp, - message(format!("add {} supertrait for", article)), + message(format!("add {article} supertrait for")), candidates.iter().map(|t| { format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) }), @@ -2836,7 +2831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_infos => { let mut msg = message(param_type.map_or_else( || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. - |param| format!("restrict type parameter `{}` with", param), + |param| format!("restrict type parameter `{param}` with"), )); for (i, trait_info) in trait_infos.iter().enumerate() { msg.push_str(&format!( @@ -2860,8 +2855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } trait_infos => { let mut msg = format!( - "the following traits define an item `{}`, but are explicitly unimplemented:", - item_name + "the following traits define an item `{item_name}`, but are explicitly unimplemented:" ); for trait_info in trait_infos { msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); @@ -3027,13 +3021,13 @@ fn print_disambiguation_help<'tcx>( .join(", "), ); let trait_name = if !fn_has_self_parameter { - format!("<{} as {}>", rcvr_ty, trait_name) + format!("<{rcvr_ty} as {trait_name}>") } else { trait_name }; - (span, format!("{}::{}{}", trait_name, item_name, args)) + (span, format!("{trait_name}::{item_name}{args}")) } else { - (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name)) + (span.with_hi(item_name.span.lo()), format!("<{rcvr_ty} as {trait_name}>::")) }; err.span_suggestion_verbose( span, @@ -3041,7 +3035,7 @@ fn print_disambiguation_help<'tcx>( "disambiguate the {} for {}", def_kind_descr, if let Some(candidate) = candidate { - format!("candidate #{}", candidate) + format!("candidate #{candidate}") } else { "the candidate".to_string() }, diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 1eae258c1b2..a283cd1abf5 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -4,7 +4,7 @@ use super::method::MethodCallee; use super::{has_expected_num_generic_args, FnCtxt}; use crate::Expectation; use rustc_ast as ast; -use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; +use rustc_errors::{self, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::ObligationCauseCode; @@ -380,33 +380,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| { - if self - .lookup_op_method( - lhs_deref_ty, - Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - let msg = format!( - "`{}{}` can be used on `{}` if you dereference the left-hand side", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, - lhs_deref_ty, - ); - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_lo(), - msg, - "*", - rustc_errors::Applicability::MachineApplicable, - ); - } - }; + let suggest_deref_binop = + |err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| { + if self + .lookup_op_method( + lhs_deref_ty, + Some((rhs_expr, rhs_ty)), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + { + let msg = format!( + "`{}{}` can be used on `{}` if you dereference the left-hand side", + op.node.as_str(), + match is_assign { + IsAssign::Yes => "=", + IsAssign::No => "", + }, + lhs_deref_ty, + ); + err.span_suggestion_verbose( + lhs_expr.span.shrink_to_lo(), + msg, + "*", + rustc_errors::Applicability::MachineApplicable, + ); + } + }; + + let suggest_different_borrow = + |err: &mut DiagnosticBuilder<'_, _>, + lhs_adjusted_ty, + lhs_new_mutbl: Option<ast::Mutability>, + rhs_adjusted_ty, + rhs_new_mutbl: Option<ast::Mutability>| { + if self + .lookup_op_method( + lhs_adjusted_ty, + Some((rhs_expr, rhs_adjusted_ty)), + Op::Binary(op, is_assign), + expected, + ) + .is_ok() + { + let op_str = op.node.as_str(); + err.note(format!("an implementation for `{lhs_adjusted_ty} {op_str} {rhs_adjusted_ty}` exists")); + + if let Some(lhs_new_mutbl) = lhs_new_mutbl + && let Some(rhs_new_mutbl) = rhs_new_mutbl + && lhs_new_mutbl.is_not() + && rhs_new_mutbl.is_not() { + err.multipart_suggestion_verbose( + "consider reborrowing both sides", + vec![ + (lhs_expr.span.shrink_to_lo(), "&*".to_string()), + (rhs_expr.span.shrink_to_lo(), "&*".to_string()) + ], + rustc_errors::Applicability::MachineApplicable, + ); + } else { + let mut suggest_new_borrow = |new_mutbl: ast::Mutability, sp: Span| { + // Can reborrow (&mut -> &) + if new_mutbl.is_not() { + err.span_suggestion_verbose( + sp.shrink_to_lo(), + "consider reborrowing this side", + "&*", + rustc_errors::Applicability::MachineApplicable, + ); + // Works on &mut but have & + } else { + err.span_help( + sp, + "consider making this expression a mutable borrow", + ); + } + }; + + if let Some(lhs_new_mutbl) = lhs_new_mutbl { + suggest_new_borrow(lhs_new_mutbl, lhs_expr.span); + } + if let Some(rhs_new_mutbl) = rhs_new_mutbl { + suggest_new_borrow(rhs_new_mutbl, rhs_expr.span); + } + } + } + }; let is_compatible_after_call = |lhs_ty, rhs_ty| { self.lookup_op_method( @@ -429,15 +489,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if is_assign == IsAssign::Yes && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - suggest_deref_binop(lhs_deref_ty); + suggest_deref_binop(&mut err, lhs_deref_ty); } else if is_assign == IsAssign::No - && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() + && let Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind() { if self.type_is_copy_modulo_regions( self.param_env, *lhs_deref_ty, ) { - suggest_deref_binop(*lhs_deref_ty); + suggest_deref_binop(&mut err, *lhs_deref_ty); + } else { + let lhs_inv_mutbl = mutbl.invert(); + let lhs_inv_mutbl_ty = Ty::new_ref( + self.tcx, + *region, + ty::TypeAndMut { + ty: *lhs_deref_ty, + mutbl: lhs_inv_mutbl, + }, + ); + + suggest_different_borrow( + &mut err, + lhs_inv_mutbl_ty, + Some(lhs_inv_mutbl), + rhs_ty, + None, + ); + + if let Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() { + let rhs_inv_mutbl = mutbl.invert(); + let rhs_inv_mutbl_ty = Ty::new_ref( + self.tcx, + *region, + ty::TypeAndMut { + ty: *rhs_deref_ty, + mutbl: rhs_inv_mutbl, + }, + ); + + suggest_different_borrow( + &mut err, + lhs_ty, + None, + rhs_inv_mutbl_ty, + Some(rhs_inv_mutbl), + ); + suggest_different_borrow( + &mut err, + lhs_inv_mutbl_ty, + Some(lhs_inv_mutbl), + rhs_inv_mutbl_ty, + Some(rhs_inv_mutbl), + ); + } } } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { is_compatible_after_call(lhs_ty, rhs_ty) diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 8bf95d4bf9a..d8eb8c71b5e 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -516,7 +516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) { if !ty.references_error() { - err.span_label(span, format!("this is of type `{}`", ty)); + err.span_label(span, format!("this is of type `{ty}`")); } } @@ -540,7 +540,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let msg = |ty| { let ty = self.resolve_vars_if_possible(ty); - format!("this is of type `{}` but it should be `char` or numeric", ty) + format!("this is of type `{ty}` but it should be `char` or numeric") }; let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| { err.span_label(first_span, msg(first_ty)); @@ -653,7 +653,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }); let pre = if in_match { "in the same arm, " } else { "" }; - err.note(format!("{}a binding must have the same type in all alternatives", pre)); + err.note(format!("{pre}a binding must have the same type in all alternatives")); self.suggest_adding_missing_ref_or_removing_ref( &mut err, span, @@ -755,7 +755,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match binding_parent { // Check that there is explicit type (ie this is not a closure param with inferred type) // so we don't suggest moving something to the type that does not exist - hir::Node::Param(hir::Param { ty_span, .. }) if binding.span != *ty_span => { + hir::Node::Param(hir::Param { ty_span, pat, .. }) if pat.span != *ty_span => { err.multipart_suggestion_verbose( format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"), vec![ @@ -924,7 +924,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match opt_def_id { Some(def_id) => match self.tcx.hir().get_if_local(def_id) { Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, body_id), .. + kind: hir::ItemKind::Const(_, _, body_id), + .. })) => match self.tcx.hir().get(body_id.hir_id) { hir::Node::Expr(expr) => { if hir::is_range_literal(expr) { @@ -1710,7 +1711,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion_verbose( qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()), "use the tuple variant pattern syntax instead", - format!("({})", sugg), + format!("({sugg})"), appl, ); return Some(err); @@ -1812,7 +1813,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { const LIMIT: usize = 3; match witnesses { [] => bug!(), - [witness] => format!("`{}`", witness), + [witness] => format!("`{witness}`"), [head @ .., tail] if head.len() < LIMIT => { let head: Vec<_> = head.iter().map(<_>::to_string).collect(); format!("`{}` and `{}`", head.join("`, `"), tail) @@ -1834,8 +1835,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "ensure that all fields are mentioned explicitly by adding the suggested fields", ); lint.note(format!( - "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - ty, + "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", )); lint @@ -1864,10 +1864,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { let fields = unmentioned_fields .iter() - .map(|(_, name)| format!("`{}`", name)) + .map(|(_, name)| format!("`{name}`")) .collect::<Vec<String>>() .join(", "); - format!("fields {}{}", fields, inaccessible) + format!("fields {fields}{inaccessible}") }; let mut err = struct_span_err!( self.tcx.sess, @@ -1876,7 +1876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "pattern does not mention {}", field_names ); - err.span_label(pat.span, format!("missing {}", field_names)); + err.span_label(pat.span, format!("missing {field_names}")); let len = unmentioned_fields.len(); let (prefix, postfix, sp) = match fields { [] => match &pat.kind { @@ -1909,11 +1909,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .map(|(_, name)| { let field_name = name.to_string(); - if is_number(&field_name) { - format!("{}: _", field_name) - } else { - field_name - } + if is_number(&field_name) { format!("{field_name}: _") } else { field_name } }) .collect::<Vec<_>>() .join(", "), @@ -1930,7 +1926,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { s = pluralize!(len), them = if len == 1 { "it" } else { "them" }, ), - format!("{}..{}", prefix, postfix), + format!("{prefix}..{postfix}"), Applicability::MachineApplicable, ); err diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index f77a4d16b5f..be939560c45 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -109,11 +109,11 @@ impl MigrationWarningReason { fn migration_message(&self) -> String { let base = "changes to closure capture in Rust 2021 will affect"; if !self.auto_traits.is_empty() && self.drop_order { - format!("{} drop order and which traits the closure implements", base) + format!("{base} drop order and which traits the closure implements") } else if self.drop_order { - format!("{} drop order", base) + format!("{base} drop order") } else { - format!("{} which traits the closure implements", base) + format!("{base} which traits the closure implements") } } } @@ -824,8 +824,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); let diagnostic_msg = format!( - "add a dummy let to cause {} to be fully captured", - migrated_variables_concat + "add a dummy let to cause {migrated_variables_concat} to be fully captured" ); let closure_span = self.tcx.hir().span_with_body(closure_hir_id); @@ -932,7 +931,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { var_hir_id: hir::HirId, closure_clause: hir::CaptureBy, ) -> Option<FxIndexMap<UpvarMigrationInfo, UnordSet<&'static str>>> { - let auto_traits_def_id = vec![ + let auto_traits_def_id = [ self.tcx.lang_items().clone_trait(), self.tcx.lang_items().sync_trait(), self.tcx.get_diagnostic_item(sym::Send), @@ -1943,7 +1942,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String let mut projections_str = String::new(); for (i, item) in place.projections.iter().enumerate() { let proj = match item.kind { - ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b), + ProjectionKind::Field(a, b) => format!("({a:?}, {b:?})"), ProjectionKind::Deref => String::from("Deref"), ProjectionKind::Index => String::from("Index"), ProjectionKind::Subslice => String::from("Subslice"), @@ -1966,7 +1965,7 @@ fn construct_capture_kind_reason_string<'tcx>( let capture_kind_str = match capture_info.capture_kind { ty::UpvarCapture::ByValue => "ByValue".into(), - ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), + ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"), }; format!("{place_str} captured as {capture_kind_str} here") @@ -1987,7 +1986,7 @@ fn construct_capture_info_string<'tcx>( let capture_kind_str = match capture_info.capture_kind { ty::UpvarCapture::ByValue => "ByValue".into(), - ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), + ty::UpvarCapture::ByRef(kind) => format!("{kind:?}"), }; format!("{place_str} -> {capture_kind_str}") } @@ -2001,7 +2000,7 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( tcx: TyCtxt<'_>, closure_id: hir::HirId, ) -> bool { - if tcx.sess.rust_2021() { + if tcx.sess.at_least_rust_2021() { return false; } @@ -2247,5 +2246,5 @@ fn truncate_capture_for_optimization( fn enable_precise_capture(span: Span) -> bool { // We use span here to ensure that if the closure was generated by a macro with a different // edition. - span.rust_2021() + span.at_least_rust_2021() } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index cf6c7f70de3..6f47623ec43 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -3,7 +3,6 @@ // substitutions. use crate::FnCtxt; -use hir::def_id::LocalDefId; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; @@ -11,13 +10,12 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; use std::mem; -use std::ops::ControlFlow; /////////////////////////////////////////////////////////////////////////// // Entry point @@ -219,7 +217,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been // emitted, so we delay an ICE if none have. (#64638) - self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{:?}`", base)); + self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{base:?}`")); } if let Some(base_ty) = base_ty && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() @@ -233,7 +231,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { Ty::new_error_with_message( self.fcx.tcx, e.span, - format!("bad index {:?} for base: `{:?}`", index, base), + format!("bad index {index:?} for base: `{base:?}`"), ) }); if self.is_builtin_index(e, base_ty_inner, index_ty) { @@ -490,10 +488,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let span = self.tcx().hir().span(hir_id); // We need to buffer the errors in order to guarantee a consistent // order when emitting them. - let err = self - .tcx() - .sess - .struct_span_err(span, format!("user args: {:?}", user_args)); + let err = + self.tcx().sess.struct_span_err(span, format!("user args: {user_args:?}")); err.buffer(&mut errors_buffer); } } @@ -565,23 +561,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); - struct RecursionChecker { - def_id: LocalDefId, - } - impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for RecursionChecker { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *t.kind() { - if def_id == self.def_id.to_def_id() { - return ControlFlow::Break(()); - } - } - t.super_visit_with(self) - } - } - if hidden_type - .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) - .is_break() + if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() + && alias_ty.def_id == opaque_type_key.def_id.to_def_id() + && alias_ty.args == opaque_type_key.args { continue; } diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 52a84b204d0..5e7ae3ecdb8 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -241,16 +241,16 @@ fn dump_graph(query: &DepGraphQuery) { { // dump a .txt file with just the edges: - let txt_path = format!("{}.txt", path); + let txt_path = format!("{path}.txt"); let mut file = BufWriter::new(File::create(&txt_path).unwrap()); for (source, target) in &edges { - write!(file, "{:?} -> {:?}\n", source, target).unwrap(); + write!(file, "{source:?} -> {target:?}\n").unwrap(); } } { // dump a .dot file in graphviz format: - let dot_path = format!("{}.dot", path); + let dot_path = format!("{path}.dot"); let mut v = Vec::new(); dot::render(&GraphvizDepGraph(nodes, edges), &mut v).unwrap(); fs::write(dot_path, v).unwrap(); @@ -285,7 +285,7 @@ impl<'a> dot::Labeller<'a> for GraphvizDepGraph { dot::Id::new("DependencyGraph").unwrap() } fn node_id(&self, n: &DepKind) -> dot::Id<'_> { - let s: String = format!("{:?}", n) + let s: String = format!("{n:?}") .chars() .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' }) .collect(); @@ -293,7 +293,7 @@ impl<'a> dot::Labeller<'a> for GraphvizDepGraph { dot::Id::new(s).unwrap() } fn node_label(&self, n: &DepKind) -> dot::LabelText<'_> { - dot::LabelText::label(format!("{:?}", n)) + dot::LabelText::label(format!("{n:?}")) } } diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index f9cd01fd80d..5dd06c6ca4f 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -300,7 +300,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { }, _ => self.tcx.sess.emit_fatal(errors::UndefinedCleanDirty { span: attr.span, - kind: format!("{:?}", node), + kind: format!("{node:?}"), }), }; let labels = diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 929a1e149b6..1111a1a17e2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -427,13 +427,11 @@ fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result<bo if sess.opts.unstable_opts.incremental_info { eprintln!( "[incremental] session directory: \ - {} files hard-linked", - files_linked + {files_linked} files hard-linked" ); eprintln!( "[incremental] session directory: \ - {} files copied", - files_copied + {files_copied} files copied" ); } @@ -604,7 +602,7 @@ fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId let stable_crate_id = base_n::encode(stable_crate_id.as_u64() as u128, INT_ENCODE_BASE); - let crate_name = format!("{}-{}", crate_name, stable_crate_id); + let crate_name = format!("{crate_name}-{stable_crate_id}"); incr_dir.join(crate_name) } diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 1ffa8633afd..a7e045e1e89 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -210,10 +210,8 @@ impl<'a> SourceKindMultiSuggestion<'a> { _ => ("", ""), }; let (start_span, start_span_code, end_span) = match should_wrap_expr { - Some(end_span) => { - (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post), Some(end_span)) - } - None => (data.span(), format!("{}{}{}", arrow, ty_info, post), None), + Some(end_span) => (data.span(), format!("{arrow}{ty_info}{post}{{ "), Some(end_span)), + None => (data.span(), format!("{arrow}{ty_info}{post}"), None), }; Self::ClosureReturn { start_span, start_span_code, end_span } } @@ -404,9 +402,9 @@ impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { debug!(?lifetime_sub.ident.span); let make_suggestion = |ident: Ident| { let sugg = if ident.name == kw::Empty { - format!("{}, ", suggestion_param_name) + format!("{suggestion_param_name}, ") } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { - format!("{} ", suggestion_param_name) + format!("{suggestion_param_name} ") } else { suggestion_param_name.clone() }; @@ -419,9 +417,9 @@ impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { let new_param_suggestion = if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) { - (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) + (first.span.shrink_to_lo(), format!("{suggestion_param_name}, ")) } else { - (generics.span, format!("<{}>", suggestion_param_name)) + (generics.span, format!("<{suggestion_param_name}>")) }; suggestions.push(new_param_suggestion); @@ -1320,7 +1318,7 @@ impl AddToDiagnostic for SuggestTuplePatternMany { message, self.compatible_variants.into_iter().map(|variant| { vec![ - (self.cause_span.shrink_to_lo(), format!("{}(", variant)), + (self.cause_span.shrink_to_lo(), format!("{variant}(")), (self.cause_span.shrink_to_hi(), ")".to_string()), ] }), diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 7328241dfbc..bd168f047fa 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -80,7 +80,7 @@ impl<'a> DescriptionCtx<'a> { // We shouldn't really be having unification failures with ReVar // and ReLateBound though. ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => { - (alt_span, "revar", format!("{:?}", region)) + (alt_span, "revar", format!("{region:?}")) } }; Some(DescriptionCtx { span, kind, arg }) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 1fd5d2adf80..54d901f20da 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -205,7 +205,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { // `delay_span_bug` to allow type error over an ICE. canonicalizer.tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, - format!("unexpected region in query response: `{:?}`", r), + format!("unexpected region in query response: `{r:?}`"), ); r } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 38f8ad744a6..ddc8e7e50eb 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -177,7 +177,7 @@ impl<'tcx> InferCtxt<'tcx> { self.tcx.check_tys_might_be_eq(canonical).map_err(|_| { self.tcx.sess.delay_span_bug( DUMMY_SP, - format!("cannot relate consts of different types (a={:?}, b={:?})", a, b,), + format!("cannot relate consts of different types (a={a:?}, b={b:?})",), ) }) }); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 8e3c76d6a4b..5a78790ec6e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -238,7 +238,7 @@ fn msg_span_from_named_region<'tcx>( let text = if name == kw::UnderscoreLifetime { "the anonymous lifetime as defined here".to_string() } else { - format!("the lifetime `{}` as defined here", name) + format!("the lifetime `{name}` as defined here") }; (text, Some(span)) } @@ -250,7 +250,7 @@ fn msg_span_from_named_region<'tcx>( }) ), _ => ( - format!("the lifetime `{}` as defined here", region), + format!("the lifetime `{region}` as defined here"), Some(tcx.def_span(scope)), ), } @@ -264,11 +264,11 @@ fn msg_span_from_named_region<'tcx>( ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(Some(span)), .. }, .. - }) => (format!("the anonymous lifetime defined here"), Some(span)), + }) => ("the anonymous lifetime defined here".to_owned(), Some(span)), ty::RePlaceholder(ty::PlaceholderRegion { bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon(None), .. }, .. - }) => (format!("an anonymous lifetime"), None), + }) => ("an anonymous lifetime".to_owned(), None), _ => bug!("{:?}", region), } } @@ -280,7 +280,7 @@ fn emit_msg_span( span: Option<Span>, suffix: &str, ) { - let message = format!("{}{}{}", prefix, description, suffix); + let message = format!("{prefix}{description}{suffix}"); if let Some(span) = span { err.span_note(span, message); @@ -296,7 +296,7 @@ fn label_msg_span( span: Option<Span>, suffix: &str, ) { - let message = format!("{}{}{}", prefix, description, suffix); + let message = format!("{prefix}{description}{suffix}"); if let Some(span) = span { err.span_label(span, message); @@ -333,7 +333,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( explain_free_region( tcx, &mut err, - &format!("hidden type `{}` captures ", hidden_ty), + &format!("hidden type `{hidden_ty}` captures "), hidden_region, "", ); @@ -345,7 +345,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( fn_returns, hidden_region.to_string(), None, - format!("captures `{}`", hidden_region), + format!("captures `{hidden_region}`"), None, Some(reg_info.def_id), ) @@ -373,7 +373,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( note_and_explain_region( tcx, &mut err, - &format!("hidden type `{}` captures ", hidden_ty), + &format!("hidden type `{hidden_ty}` captures "), hidden_region, "", None, @@ -716,7 +716,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { err.span_label(span, format!("this is an iterator with items of type `{}`", args.type_at(0))); } else { - err.span_label(span, format!("this expression has type `{}`", ty)); + err.span_label(span, format!("this expression has type `{ty}`")); } } if let Some(ty::error::ExpectedFound { found, .. }) = exp_found @@ -726,7 +726,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.span_suggestion( span, "consider dereferencing the boxed value", - format!("*{}", snippet), + format!("*{snippet}"), Applicability::MachineApplicable, ); } @@ -785,13 +785,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if prior_arms.len() <= 4 { for sp in prior_arms { any_multiline_arm |= source_map.is_multiline(*sp); - err.span_label(*sp, format!("this is found to be of type `{}`", t)); + err.span_label(*sp, format!("this is found to be of type `{t}`")); } } else if let Some(sp) = prior_arms.last() { any_multiline_arm |= source_map.is_multiline(*sp); err.span_label( *sp, - format!("this and all prior arms are found to be of type `{}`", t), + format!("this and all prior arms are found to be of type `{t}`"), ); } let outer = if any_multiline_arm || !source_map.is_multiline(cause.span) { @@ -1661,7 +1661,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .. })) = values { - Cow::from(format!("expected this to be `{}`", expected)) + Cow::from(format!("expected this to be `{expected}`")) } else { terr.to_string(self.tcx) }; @@ -2068,7 +2068,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { visitor.visit_body(body); visitor.result.map(|r| &r.peel_refs().kind) } - Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) => { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => { Some(&ty.peel_refs().kind) } _ => None, @@ -2354,7 +2354,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span) && snip.starts_with(' ') { - format!("{new_lt}") + new_lt.to_string() } else { format!("{new_lt} ") } @@ -2368,13 +2368,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let labeled_user_string = match bound_kind { - GenericKind::Param(ref p) => format!("the parameter type `{}`", p), + GenericKind::Param(ref p) => format!("the parameter type `{p}`"), GenericKind::Alias(ref p) => match p.kind(self.tcx) { ty::AliasKind::Projection | ty::AliasKind::Inherent => { - format!("the associated type `{}`", p) + format!("the associated type `{p}`") } - ty::AliasKind::Weak => format!("the type alias `{}`", p), - ty::AliasKind::Opaque => format!("the opaque type `{}`", p), + ty::AliasKind::Weak => format!("the type alias `{p}`"), + ty::AliasKind::Opaque => format!("the opaque type `{p}`"), }, }; @@ -2388,7 +2388,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", bound_kind, sub), + &format!("`{bound_kind}: {sub}`"), ); } @@ -2402,7 +2402,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let msg = "consider adding an explicit lifetime bound"; if let Some((sp, has_lifetimes)) = type_param_span { let suggestion = - if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) }; + if has_lifetimes { format!(" + {sub}") } else { format!(": {sub}") }; let mut suggestions = vec![(sp, suggestion)]; for add_lt_sugg in add_lt_suggs.into_iter().flatten() { suggestions.push(add_lt_sugg); @@ -2413,7 +2413,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Applicability::MaybeIncorrect, // Issue #41966 ); } else { - let consider = format!("{} `{}: {}`...", msg, bound_kind, sub); + let consider = format!("{msg} `{bound_kind}: {sub}`..."); err.help(consider); } } @@ -2422,13 +2422,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| { let msg = "consider introducing an explicit lifetime bound"; if let Some((sp, has_lifetimes)) = type_param_span { - let suggestion = if has_lifetimes { - format!(" + {}", new_lt) - } else { - format!(": {}", new_lt) - }; + let suggestion = + if has_lifetimes { format!(" + {new_lt}") } else { format!(": {new_lt}") }; let mut sugg = - vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))]; + vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {new_lt}"))]; for lt in add_lt_suggs.clone().into_iter().flatten() { sugg.push(lt); sugg.rotate_right(1); @@ -2508,7 +2505,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "{} may not live long enough", labeled_user_string ); - let pred = format!("{}: {}", bound_kind, sub); + let pred = format!("{bound_kind}: {sub}"); let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,); err.span_suggestion( generics.tail_span_for_predicate_suggestion(), @@ -2564,7 +2561,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { note_and_explain_region( self.tcx, &mut err, - &format!("{} must be valid for ", labeled_user_string), + &format!("{labeled_user_string} must be valid for "), sub, "...", None, @@ -2814,10 +2811,10 @@ impl<'tcx> InferCtxt<'tcx> { br_string(br), self.tcx.associated_item(def_id).name ), - infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name), + infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{name}`"), infer::UpvarRegion(ref upvar_id, _) => { let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - format!(" for capture of `{}` by closure", var_name) + format!(" for capture of `{var_name}` by closure") } infer::Nll(..) => bug!("NLL variable found in lexical phase"), }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 36b56fe782c..9dfa45858a7 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -246,7 +246,7 @@ fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String { } else { format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder(), None)) }; - format!("fn({}){}", args, ret) + format!("fn({args}){ret}") } impl<'tcx> InferCtxt<'tcx> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index cc92bfbfdfe..d08b6ba5e47 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -235,10 +235,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let arg = match param.param.pat.simple_ident() { - Some(simple_ident) => format!("argument `{}`", simple_ident), + Some(simple_ident) => format!("argument `{simple_ident}`"), None => "the argument".to_string(), }; - let captures = format!("captures data from {}", arg); + let captures = format!("captures data from {arg}"); suggest_new_region_bound( tcx, &mut err, @@ -269,11 +269,11 @@ pub fn suggest_new_region_bound( // FIXME: account for the need of parens in `&(dyn Trait + '_)` let consider = "consider changing"; let declare = "to declare that"; - let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name); + let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound"); let explicit_static = - arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg)); + arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}")); let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; - let plus_lt = format!(" + {}", lifetime_name); + let plus_lt = format!(" + {lifetime_name}"); for fn_return in fn_returns { if fn_return.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. @@ -333,11 +333,7 @@ pub fn suggest_new_region_bound( } else { None }; - let name = if let Some(name) = &existing_lt_name { - format!("{}", name) - } else { - format!("'a") - }; + let name = if let Some(name) = &existing_lt_name { name } else { "'a" }; // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used. // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any if let Some(id) = scope_def_id @@ -350,7 +346,7 @@ pub fn suggest_new_region_bound( if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_) (p.span.shrink_to_hi(),format!("{name} ")) } else { // Underscore (elided with '_) - (p.span, format!("{name}")) + (p.span, name.to_string()) } ) .collect::<Vec<_>>() @@ -387,12 +383,7 @@ pub fn suggest_new_region_bound( if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), - format!( - "{declare} the trait object {captures}, {explicit}", - declare = declare, - captures = captures, - explicit = explicit, - ), + format!("{declare} the trait object {captures}, {explicit}",), &plus_lt, Applicability::MaybeIncorrect, ); @@ -404,7 +395,7 @@ pub fn suggest_new_region_bound( if let Some(explicit_static) = &explicit_static { err.span_suggestion_verbose( lt.ident.span, - format!("{} the trait object's {}", consider, explicit_static), + format!("{consider} the trait object's {explicit_static}"), &lifetime_name, Applicability::MaybeIncorrect, ); diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 7144084c78e..8d3cd23b7fa 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -227,7 +227,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span, impl_item_def_id, trait_item_def_id, - &format!("`{}: {}`", sup, sub), + &format!("`{sup}: {sub}`"), ); // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) @@ -243,12 +243,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { let mut err = self.report_concrete_failure(*parent, sub, sup); - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{}` from trait", item_name), - ); + + // Don't mention the item name if it's an RPITIT, since that'll just confuse + // folks. + if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { + let trait_item_span = self.tcx.def_span(trait_item_def_id); + let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); + err.span_label( + trait_item_span, + format!("definition of `{item_name}` from trait"), + ); + } + self.suggest_copy_trait_method_bounds( trait_item_def_id, impl_item_def_id, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 8e3f6b97e8f..372539d73b1 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -47,7 +47,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { diag.span_suggestion( sp, "use a float literal", - format!("{}.0", snippet), + format!("{snippet}.0"), MachineApplicable, ); } @@ -134,7 +134,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if matched_end_of_args { // Append suggestion to the end of our args - let path = format!(", {}{} = {}",item_name, item_args, p); + let path = format!(", {item_name}{item_args} = {p}"); note = !suggest_constraining_type_param( tcx, generics, @@ -148,7 +148,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // Suggest adding a bound to an existing trait // or if the trait doesn't exist, add the trait // and the suggested bounds. - let path = format!("<{}{} = {}>", item_name, item_args, p); + let path = format!("<{item_name}{item_args} = {p}>"); note = !suggest_constraining_type_param( tcx, generics, @@ -213,8 +213,7 @@ impl<T> Trait<T> for X { } diag.help(format!( "every closure has a distinct type and so could not always match the \ - caller-chosen type of parameter `{}`", - p + caller-chosen type of parameter `{p}`" )); } (ty::Param(p), _) | (_, ty::Param(p)) => { diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index ce5d3791a48..f1d53cb59cd 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -575,12 +575,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if param_hir.pat.span == param_hir.ty_span { // for `|x|`, `|_|`, `|x: impl Foo|` let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; }; - suggestion += &format!("{}: &_", pat); + suggestion += &format!("{pat}: &_"); } else { // for `|x: ty|`, `|_: ty|` let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; }; let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span) else { return; }; - suggestion += &format!("{}: &{}", pat, ty); + suggestion += &format!("{pat}: &{ty}"); } has_suggestion = true; } else { diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 485e34fe2bf..be424424f3a 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -837,9 +837,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.var_infos[node_idx].origin.span(), format!( "collect_error_for_expanding_node() could not find \ - error for var {:?} in universe {:?}, lower_bounds={:#?}, \ - upper_bounds={:#?}", - node_idx, node_universe, lower_bounds, upper_bounds + error for var {node_idx:?} in universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \ + upper_bounds={upper_bounds:#?}" ), ); } diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 71c07f31bc9..c8049164391 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -557,7 +557,7 @@ where // Forbid inference variables in the RHS. self.infcx.tcx.sess.delay_span_bug( self.delegate.span(), - format!("unexpected inference var {:?}", b,), + format!("unexpected inference var {b:?}",), ); Ok(a) } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 945136fbff2..9c90b704586 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -619,13 +619,6 @@ impl<'tcx> InferCtxt<'tcx> { { hidden_ty } - // FIXME(RPITIT): This can go away when we move to associated types - // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too. - ty::Alias(ty::Projection, ty::AliasTy { def_id: def_id2, args: args2, .. }) - if def_id == def_id2 && args == args2 => - { - hidden_ty - } _ => ty, }, lt_op: |lt| lt, diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 0681f414ce9..f36802e1284 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -253,7 +253,7 @@ where // this point it never will be self.tcx.sess.delay_span_bug( origin.span(), - format!("unresolved inference variable in outlives: {:?}", v), + format!("unresolved inference variable in outlives: {v:?}"), ); } } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 2bc6546ba28..4279d0ab7ab 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -179,7 +179,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { // this point it never will be self.tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, - format!("unresolved inference variable in outlives: {:?}", v), + format!("unresolved inference variable in outlives: {v:?}"), ); // add a bound that never holds VerifyBound::AnyBound(vec![]) diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs index dd65f66ccd1..b6ff8f2f512 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -425,9 +425,11 @@ impl<'tcx> MiniGraph<'tcx> { } } } else { - for (constraint, _origin) in ®ion_constraints.data().constraints { - each_constraint(constraint) - } + region_constraints + .data() + .constraints + .keys() + .for_each(|constraint| each_constraint(constraint)); } } diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index 613da8a0b45..708c51cabeb 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -704,8 +704,8 @@ impl fmt::Debug for RegionSnapshot { impl<'tcx> fmt::Debug for GenericKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - GenericKind::Param(ref p) => write!(f, "{:?}", p), - GenericKind::Alias(ref p) => write!(f, "{:?}", p), + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p:?}"), } } } @@ -713,8 +713,8 @@ impl<'tcx> fmt::Debug for GenericKind<'tcx> { impl<'tcx> fmt::Display for GenericKind<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - GenericKind::Param(ref p) => write!(f, "{}", p), - GenericKind::Alias(ref p) => write!(f, "{}", p), + GenericKind::Param(ref p) => write!(f, "{p}"), + GenericKind::Alias(ref p) => write!(f, "{p}"), } } } diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index 9f440f39849..e72a43630e9 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -28,11 +28,11 @@ impl<'tcx> InferCtxt<'tcx> { if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) { if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) { let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label(span, format!("definition of `{}` from trait", item_name)); + err.span_label(span, format!("definition of `{item_name}` from trait")); } } - err.span_label(error_span, format!("impl has extra requirement {}", requirement)); + err.span_label(error_span, format!("impl has extra requirement {requirement}")); err } @@ -56,7 +56,7 @@ pub fn report_object_safety_error<'tcx>( "the trait `{}` cannot be made into an object", trait_str ); - err.span_label(span, format!("`{}` cannot be made into an object", trait_str)); + err.span_label(span, format!("`{trait_str}` cannot be made into an object")); let mut reported_violations = FxIndexSet::default(); let mut multi_span = vec![]; diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 626dd9359a1..a9da6104b38 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -79,7 +79,6 @@ impl<'tcx> PredicateObligation<'tcx> { } pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> { - self.param_env = self.param_env.without_const(); if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() { self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred.without_const())))); } @@ -88,14 +87,6 @@ impl<'tcx> PredicateObligation<'tcx> { } impl<'tcx> PolyTraitObligation<'tcx> { - /// Returns `true` if the trait predicate is considered `const` in its ParamEnv. - pub fn is_const(&self) -> bool { - matches!( - (self.predicate.skip_binder().constness, self.param_env.constness()), - (ty::BoundConstness::ConstIfConst, hir::Constness::Const) - ) - } - pub fn derived_cause( &self, variant: impl FnOnce(DerivedObligationCause<'tcx>) -> ObligationCauseCode<'tcx>, diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index e375d611936..afba2e50a23 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -190,7 +190,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { } let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy { ty: value, complete: None }); - assert!(!fresh_key, "never started projecting `{:?}`", key); + assert!(!fresh_key, "never started projecting `{key:?}`"); } /// Mark the relevant projection cache key as having its derived obligations @@ -229,7 +229,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { /// be different). pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous); - assert!(!fresh, "never started projecting `{:?}`", key); + assert!(!fresh, "never started projecting `{key:?}`"); } /// Indicates that while trying to normalize `key`, `key` was required to @@ -237,14 +237,14 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { /// an error here. pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map().insert(key, ProjectionCacheEntry::Recur); - assert!(!fresh, "never started projecting `{:?}`", key); + assert!(!fresh, "never started projecting `{key:?}`"); } /// Indicates that trying to normalize `key` resulted in /// error. pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { let fresh = self.map().insert(key, ProjectionCacheEntry::Error); - assert!(!fresh, "never started projecting `{:?}`", key); + assert!(!fresh, "never started projecting `{key:?}`"); } } diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 1563d92af0e..8a7c59da09e 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -38,17 +38,17 @@ impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> { impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - super::CodeSelectionError(ref e) => write!(f, "{:?}", e), - super::CodeProjectionError(ref e) => write!(f, "{:?}", e), + super::CodeSelectionError(ref e) => write!(f, "{e:?}"), + super::CodeProjectionError(ref e) => write!(f, "{e:?}"), super::CodeSubtypeError(ref a, ref b) => { - write!(f, "CodeSubtypeError({:?}, {:?})", a, b) + write!(f, "CodeSubtypeError({a:?}, {b:?})") } super::CodeConstEquateError(ref a, ref b) => { - write!(f, "CodeConstEquateError({:?}, {:?})", a, b) + write!(f, "CodeConstEquateError({a:?}, {b:?})") } super::CodeAmbiguity { overflow: false } => write!(f, "Ambiguity"), super::CodeAmbiguity { overflow: true } => write!(f, "Overflow"), - super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle), + super::CodeCycle(ref cycle) => write!(f, "Cycle({cycle:?})"), } } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 074ff7ec97f..87ba6b3ec50 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -25,6 +25,13 @@ impl<'tcx> PredicateSet<'tcx> { Self { tcx, set: Default::default() } } + /// Adds a predicate to the set. + /// + /// Returns whether the predicate was newly inserted. That is: + /// - If the set did not previously contain this predicate, `true` is returned. + /// - If the set already contained this predicate, `false` is returned, + /// and the set is not modified: original predicate is not replaced, + /// and the predicate passed as argument is dropped. pub fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool { // We have to be careful here because we want // diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 7826d42dcb2..2c7438ed9db 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [lib] [dependencies] -atty = "0.2.13" libloading = "0.7.1" tracing = "0.1" rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 3f420f19efe..5b417e008cf 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -251,6 +251,7 @@ pub struct Config { pub input: Input, pub output_dir: Option<PathBuf>, pub output_file: Option<OutFileName>, + pub ice_file: Option<PathBuf>, pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>, pub locale_resources: &'static [&'static str], @@ -315,6 +316,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se config.lint_caps, config.make_codegen_backend, registry.clone(), + config.ice_file, ); if let Some(parse_sess_created) = config.parse_sess_created { @@ -346,7 +348,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se ) } -pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) { +pub fn try_print_query_stack( + handler: &Handler, + num_frames: Option<usize>, + file: Option<std::fs::File>, +) { eprintln!("query stack during panic:"); // Be careful relying on global state here: this code is called from @@ -358,7 +364,8 @@ pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) { QueryCtxt::new(icx.tcx), icx.query, handler, - num_frames + num_frames, + file, )) } else { 0 diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 09141afd137..e3d66d18388 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -67,6 +67,7 @@ fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Se None, None, "", + None, ); (sess, cfg) } @@ -714,6 +715,7 @@ fn test_unstable_options_tracking_hash() { untracked!(perf_stats, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); + untracked!(print_codegen_stats, true); untracked!(print_llvm_passes, true); untracked!(print_mono_items, Some(String::from("abc"))); untracked!(print_type_sizes, true); @@ -738,6 +740,7 @@ fn test_unstable_options_tracking_hash() { untracked!(unstable_options, true); untracked!(validate_mir, true); untracked!(verbose, true); + untracked!(write_long_types_to_disk, false); // tidy-alphabetical-end macro_rules! tracked { diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 035ea2414f7..ad35dbbc8f9 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -70,6 +70,7 @@ pub fn create_session( Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, >, descriptions: Registry, + ice_file: Option<PathBuf>, ) -> (Session, Box<dyn CodegenBackend>) { let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend { make_codegen_backend(&sopts) @@ -111,6 +112,7 @@ pub fn create_session( file_loader, target_override, rustc_version_str().unwrap_or("unknown"), + ice_file, ); codegen_backend.init(&sess); @@ -517,7 +519,8 @@ fn multiple_output_types_to_stdout( output_types: &OutputTypes, single_output_file_is_stdout: bool, ) -> bool { - if atty::is(atty::Stream::Stdout) { + use std::io::IsTerminal; + if std::io::stdout().is_terminal() { // If stdout is a tty, check if multiple text output types are // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar` let named_text_types = output_types diff --git a/compiler/rustc_lexer/Cargo.toml b/compiler/rustc_lexer/Cargo.toml index 23294dc2e1b..2211ac1c8a7 100644 --- a/compiler/rustc_lexer/Cargo.toml +++ b/compiler/rustc_lexer/Cargo.toml @@ -16,7 +16,11 @@ Rust lexer used by rustc. No stability guarantees are provided. # Note that this crate purposefully does not depend on other rustc crates [dependencies] unicode-xid = "0.2.0" -unic-emoji-char = "0.9.0" + +[dependencies.unicode-properties] +version = "0.1.0" +default-features = false +features = ["emoji"] [dev-dependencies] expect-test = "1.4.0" diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index eceef59802e..aba7f95487e 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -24,6 +24,10 @@ impl<'a> Cursor<'a> { } } + pub fn as_str(&self) -> &'a str { + self.chars.as_str() + } + /// Returns the last eaten symbol (or `'\0'` in release builds). /// (For debug assertions only.) pub(crate) fn prev(&self) -> char { diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 29335a8c0f4..43dfd34a6ff 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -34,6 +34,7 @@ pub use crate::cursor::Cursor; use self::LiteralKind::*; use self::TokenKind::*; use crate::cursor::EOF_CHAR; +use unicode_properties::UnicodeEmoji; /// Parsed token. /// It doesn't contain information about data that has been parsed, @@ -367,6 +368,13 @@ impl Cursor<'_> { Some(|terminated| Byte { terminated }), ), + // c-string literal, raw c-string literal or identifier. + 'c' => self.c_or_byte_string( + |terminated| CStr { terminated }, + |n_hashes| RawCStr { n_hashes }, + None, + ), + // Identifier (this should be checked after other variant that can // start as identifier). c if is_id_start(c) => self.ident_or_unknown_prefix(), @@ -421,9 +429,7 @@ impl Cursor<'_> { Literal { kind, suffix_start } } // Identifier starting with an emoji. Only lexed for graceful error recovery. - c if !c.is_ascii() && unic_emoji_char::is_emoji(c) => { - self.fake_ident_or_unknown_prefix() - } + c if !c.is_ascii() && c.is_emoji_char() => self.fake_ident_or_unknown_prefix(), _ => Unknown, }; let res = Token::new(token_kind, self.pos_within_token()); @@ -507,9 +513,7 @@ impl Cursor<'_> { // we see a prefix here, it is definitely an unknown prefix. match self.first() { '#' | '"' | '\'' => UnknownPrefix, - c if !c.is_ascii() && unic_emoji_char::is_emoji(c) => { - self.fake_ident_or_unknown_prefix() - } + c if !c.is_ascii() && c.is_emoji_char() => self.fake_ident_or_unknown_prefix(), _ => Ident, } } @@ -518,7 +522,7 @@ impl Cursor<'_> { // Start is already eaten, eat the rest of identifier. self.eat_while(|c| { unicode_xid::UnicodeXID::is_xid_continue(c) - || (!c.is_ascii() && unic_emoji_char::is_emoji(c)) + || (!c.is_ascii() && c.is_emoji_char()) || c == '\u{200d}' }); // Known prefixes must have been handled earlier. So if diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs index c9ad54d8d98..717b042fbda 100644 --- a/compiler/rustc_lexer/src/unescape.rs +++ b/compiler/rustc_lexer/src/unescape.rs @@ -372,7 +372,7 @@ where callback(start..end, EscapeError::MultipleSkippedLinesWarning); } let tail = &tail[first_non_space..]; - if let Some(c) = tail.chars().nth(0) { + if let Some(c) = tail.chars().next() { if c.is_whitespace() { // For error reporting, we would like the span to contain the character that was not // skipped. The +1 is necessary to account for the leading \ that started the escape. diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 2c92277b50d..16e17fc9d6a 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -155,8 +155,6 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` -lint_cast_ref_to_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` - lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name} lint_check_name_unknown = unknown lint: `{$lint_name}` @@ -267,8 +265,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 @@ -322,6 +318,8 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable +lint_invalid_reference_casting = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` + lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead @@ -412,8 +410,8 @@ lint_non_upper_case_global = {$sort} `{$name}` should have an upper case name .label = should have an UPPER_CASE name lint_noop_method_call = call to `.{$method}()` on a reference in this situation does nothing - .label = unnecessary method call - .note = the type `{$receiver_ty}` which `{$method}` is being called on is the same as the type returned from `{$method}`, so the method call does not do anything and can be removed + .suggestion = remove this redundant call + .note = the type `{$orig_ty}` does not implement `{$trait_}`, so calling `{$method}` on `&{$orig_ty}` copies the reference, which does not do anything and can be removed lint_only_cast_u8_to_char = only `u8` can be cast into `char` .suggestion = use a `char` literal instead diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 84aca80b0de..e6917f4b2d3 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1458,15 +1458,20 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else { return }; - if cx.tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() { - // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);` + let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; + + if cx.tcx.features().lazy_type_alias { + // Bounds of lazy type aliases are respected. return; } - if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() { - // Bounds are respected for `type X = … Type::Inherent …` + + let ty = cx.tcx.type_of(item.owner_id).skip_binder(); + if ty.has_opaque_types() || ty.has_inherent_projections() { + // Bounds of type aliases that contain opaque types or inherent projections are respected. + // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = Type::Inherent;`. return; } + // There must not be a where clause if type_alias_generics.predicates.is_empty() { return; @@ -1491,7 +1496,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { if !where_spans.is_empty() { let sub = (!suggested_changing_assoc_types).then(|| { suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty } + SuggestChangingAssocTypes { ty: hir_ty } }); cx.emit_spanned_lint( TYPE_ALIAS_BOUNDS, @@ -1507,7 +1512,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg }; let sub = (!suggested_changing_assoc_types).then(|| { suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty } + SuggestChangingAssocTypes { ty: hir_ty } }); cx.emit_spanned_lint( TYPE_ALIAS_BOUNDS, @@ -1529,9 +1534,10 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { match it.kind { - hir::ItemKind::Const(_, body_id) => { + hir::ItemKind::Const(_, _, body_id) => { let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id(); // trigger the query once for all constants since that will already report the errors + // FIXME(generic_const_items): Does this work properly with generic const items? cx.tcx.ensure().const_eval_poly(def_id); } hir::ItemKind::Static(_, _, body_id) => { @@ -1716,7 +1722,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { let end = expr_to_string(&end); let replace = match start { Some(start) => format!("&({}..={})", expr_to_string(&start), end), - None => format!("&(..={})", end), + None => format!("&(..={end})"), }; if join.edition() >= Edition::Edition2021 { cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 6522e449386..f7e56b30553 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -411,7 +411,7 @@ impl LintStore { } let complete_name = if let Some(tool_name) = tool_name { - format!("{}::{}", tool_name, lint_name) + format!("{tool_name}::{lint_name}") } else { lint_name.to_string() }; @@ -424,7 +424,7 @@ impl LintStore { // 1. The tool is currently running, so this lint really doesn't exist. // FIXME: should this handle tools that never register a lint, like rustfmt? debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>()); - let tool_prefix = format!("{}::", tool_name); + let tool_prefix = format!("{tool_name}::"); return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { self.no_lint_suggestion(&complete_name) } else { @@ -445,11 +445,11 @@ impl LintStore { } match self.by_name.get(&complete_name) { Some(Renamed(new_name, _)) => CheckLintNameResult::Warning( - format!("lint `{}` has been renamed to `{}`", complete_name, new_name), + format!("lint `{complete_name}` has been renamed to `{new_name}`"), Some(new_name.to_owned()), ), Some(Removed(reason)) => CheckLintNameResult::Warning( - format!("lint `{}` has been removed: {}", complete_name, reason), + format!("lint `{complete_name}` has been removed: {reason}"), None, ), None => match self.lint_groups.get(&*complete_name) { @@ -503,7 +503,7 @@ impl LintStore { lint_name: &str, tool_name: &str, ) -> CheckLintNameResult<'_> { - let complete_name = format!("{}::{}", tool_name, lint_name); + let complete_name = format!("{tool_name}::{lint_name}"); match self.by_name.get(&complete_name) { None => match self.lint_groups.get(&*complete_name) { // Now we are sure, that this lint exists nowhere @@ -618,12 +618,10 @@ pub trait LintContext: Sized { _ => ("", "s"), }; db.span_label(span, format!( - "this comment contains {}invisible unicode text flow control codepoint{}", - an, - s, + "this comment contains {an}invisible unicode text flow control codepoint{s}", )); for (c, span) in &spans { - db.span_label(*span, format!("{:?}", c)); + db.span_label(*span, format!("{c:?}")); } db.note( "these kind of unicode codepoints change the way text flows on \ @@ -648,7 +646,7 @@ pub trait LintContext: Sized { let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable) + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) } Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders), }; @@ -704,7 +702,7 @@ pub trait LintContext: Sized { let introduced = if is_imported { "imported" } else { "defined" }; db.span_label( span, - format!("the item `{}` is already {} here", ident, introduced), + format!("the item `{ident}` is already {introduced} here"), ); } } @@ -908,7 +906,7 @@ pub trait LintContext: Sized { BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => { db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string"); if let Some(positional_arg_for_msg) = position_sp_for_msg { - let msg = format!("this formatting argument uses named argument `{}` by position", named_arg_name); + let msg = format!("this formatting argument uses named argument `{named_arg_name}` by position"); db.span_label(positional_arg_for_msg, msg); } @@ -948,19 +946,22 @@ pub trait LintContext: Sized { Applicability::MachineApplicable, ); } + BuiltinLintDiagnostics::AmbiguousGlobImports { diag } => { + rustc_errors::report_ambiguity_error(db, diag); + } BuiltinLintDiagnostics::AmbiguousGlobReexports { name, namespace, first_reexport_span, duplicate_reexport_span } => { - db.span_label(first_reexport_span, format!("the name `{}` in the {} namespace is first re-exported here", name, namespace)); - db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace)); + db.span_label(first_reexport_span, format!("the name `{name}` in the {namespace} namespace is first re-exported here")); + db.span_label(duplicate_reexport_span, format!("but the name `{name}` in the {namespace} namespace is also re-exported here")); } BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => { - db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace)); + db.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here")); db.span_note(private_item_span, "but the private item here shadows it".to_owned()); } - BuiltinLintDiagnostics::UnusedQualifications { path_span, unqualified_path } => { + BuiltinLintDiagnostics::UnusedQualifications { removal_span } => { db.span_suggestion_verbose( - path_span, - "replace it with the unqualified path", - unqualified_path, + removal_span, + "remove the unnecessary path segments", + "", Applicability::MachineApplicable ); } @@ -1281,8 +1282,8 @@ impl<'tcx> LateContext<'tcx> { // This shouldn't ever be needed, but just in case: with_no_trimmed_paths!({ Ok(vec![match trait_ref { - Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)), - None => Symbol::intern(&format!("<{}>", self_ty)), + Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")), + None => Symbol::intern(&format!("<{self_ty}>")), }]) }) } @@ -1306,7 +1307,7 @@ impl<'tcx> LateContext<'tcx> { ))) } None => { - with_no_trimmed_paths!(Symbol::intern(&format!("<impl {}>", self_ty))) + with_no_trimmed_paths!(Symbol::intern(&format!("<impl {self_ty}>"))) } }); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index c9ee2da6fdf..fb407be1f02 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -945,7 +945,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { ); } } else { - panic!("renamed lint does not exist: {}", new_name); + panic!("renamed lint does not exist: {new_name}"); } } } @@ -978,6 +978,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Returns `true` if the lint's feature is enabled. // FIXME only emit this once for each attribute, instead of repeating it 4 times for // pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`. + #[track_caller] fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool { if let Some(feature) = lint_id.lint.feature_gate { if !self.sess.features_untracked().enabled(feature) { @@ -1015,6 +1016,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] + #[track_caller] pub(crate) fn struct_lint( &self, lint: &'static Lint, @@ -1028,6 +1030,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { struct_lint_level(self.sess, lint, level, src, span, msg, decorate) } + #[track_caller] pub fn emit_spanned_lint( &self, lint: &'static Lint, @@ -1040,6 +1043,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }); } + #[track_caller] pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) { let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index beb38dbb94c..53089294fe2 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -50,7 +50,6 @@ extern crate tracing; mod array_into_iter; pub mod builtin; -mod cast_ref_to_mut; mod context; mod deref_into_dyn_supertrait; mod drop_forget_useless; @@ -78,6 +77,7 @@ mod opaque_hidden_inferred_bound; mod pass_by_value; mod passes; mod redundant_semicolon; +mod reference_casting; mod traits; mod types; mod unused; @@ -99,7 +99,6 @@ use rustc_span::Span; use array_into_iter::ArrayIntoIter; use builtin::*; -use cast_ref_to_mut::*; use deref_into_dyn_supertrait::*; use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; @@ -119,6 +118,7 @@ use noop_method_call::*; use opaque_hidden_inferred_bound::*; use pass_by_value::*; use redundant_semicolon::*; +use reference_casting::*; use traits::*; use types::*; use unused::*; @@ -218,7 +218,7 @@ late_lint_methods!( BoxPointers: BoxPointers, PathStatements: PathStatements, LetUnderscore: LetUnderscore, - CastRefToMut: CastRefToMut, + InvalidReferenceCasting: InvalidReferenceCasting, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, NonUpperCaseGlobals: NonUpperCaseGlobals, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1dea758bb29..968172693a9 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -743,10 +743,10 @@ pub enum InvalidFromUtf8Diag { }, } -// cast_ref_to_mut.rs +// reference_casting.rs #[derive(LintDiagnostic)] -#[diag(lint_cast_ref_to_mut)] -pub struct CastRefToMutDiag; +#[diag(lint_invalid_reference_casting)] +pub struct InvalidReferenceCastingDiag; // hidden_unicode_codepoints.rs #[derive(LintDiagnostic)] @@ -776,7 +776,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels { ) -> rustc_errors::SubdiagnosticMessage, { for (c, span) in self.spans { - diag.span_label(span, format!("{:?}", c)); + diag.span_label(span, format!("{c:?}")); } } } @@ -808,7 +808,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub { spans .into_iter() .map(|(c, span)| { - let c = format!("{:?}", c); + let c = format!("{c:?}"); (span, c[1..c.len() - 1].to_string()) }) .collect(), @@ -823,7 +823,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub { "escaped", spans .into_iter() - .map(|(c, _)| format!("{:?}", c)) + .map(|(c, _)| format!("{c:?}")) .collect::<Vec<String>>() .join(", "), ); @@ -1231,8 +1231,9 @@ pub enum NonUpperCaseGlobalSub { #[note] pub struct NoopMethodCallDiag<'a> { pub method: Symbol, - pub receiver_ty: Ty<'a>, - #[label] + pub orig_ty: Ty<'a>, + pub trait_: Symbol, + #[suggestion(code = "", applicability = "machine-applicable")] pub label: Span, } diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 53fe0ceb234..84558ee1f02 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -10,6 +10,7 @@ declare_lint! { /// ### Example /// /// ```rust + /// #![feature(multiple_supertrait_upcastable)] /// trait A {} /// trait B {} /// diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 13f650c2008..bc0b9d6d818 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -18,7 +18,6 @@ declare_lint! { /// /// ```rust /// # #![allow(unused)] - /// #![warn(noop_method_call)] /// struct Foo; /// let foo = &Foo; /// let clone: &Foo = foo.clone(); @@ -34,7 +33,7 @@ declare_lint! { /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything /// as references are copy. This lint detects these calls and warns the user about them. pub NOOP_METHOD_CALL, - Allow, + Warn, "detects the use of well-known noop methods" } @@ -86,10 +85,9 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { let Some(trait_id) = cx.tcx.trait_of_item(did) else { return }; - if !matches!( - cx.tcx.get_diagnostic_name(trait_id), - Some(sym::Borrow | sym::Clone | sym::Deref) - ) { + let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return }; + + if !matches!(trait_, sym::Borrow | sym::Clone | sym::Deref) { return; }; @@ -114,11 +112,13 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { let expr_span = expr.span; let span = expr_span.with_lo(receiver.span.hi()); + let orig_ty = expr_ty.peel_refs(); + if receiver_ty == expr_ty { cx.emit_spanned_lint( NOOP_METHOD_CALL, span, - NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span }, + NoopMethodCallDiag { method: call.ident.name, orig_ty, trait_, label: span }, ); } else { match name { diff --git a/compiler/rustc_lint/src/cast_ref_to_mut.rs b/compiler/rustc_lint/src/reference_casting.rs index 82bb70bc9e7..d343aaf35d5 100644 --- a/compiler/rustc_lint/src/cast_ref_to_mut.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -3,15 +3,16 @@ use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp}; use rustc_middle::ty; use rustc_span::sym; -use crate::{lints::CastRefToMutDiag, LateContext, LateLintPass, LintContext}; +use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext}; declare_lint! { - /// The `cast_ref_to_mut` lint checks for casts of `&T` to `&mut T` + /// The `invalid_reference_casting` lint checks for casts of `&T` to `&mut T` /// without using interior mutability. /// /// ### Example /// /// ```rust,compile_fail + /// # #![deny(invalid_reference_casting)] /// fn x(r: &i32) { /// unsafe { /// *(r as *const i32 as *mut i32) += 1; @@ -28,14 +29,14 @@ declare_lint! { /// /// `UnsafeCell` is the only way to obtain aliasable data that is considered /// mutable. - CAST_REF_TO_MUT, - Deny, + INVALID_REFERENCE_CASTING, + Allow, "casts of `&T` to `&mut T` without interior mutability" } -declare_lint_pass!(CastRefToMut => [CAST_REF_TO_MUT]); +declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); -impl<'tcx> LateLintPass<'tcx> for CastRefToMut { +impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { return; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for CastRefToMut { let e = e.peel_blocks(); if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() { - cx.emit_spanned_lint(CAST_REF_TO_MUT, expr.span, CastRefToMutDiag); + cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag); } } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index cc8a3408004..226d01b79a8 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -966,12 +966,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { let field_ty = field.ty(self.cx.tcx, args); - if field_ty.has_opaque_types() { - self.check_type_for_ffi(cache, field_ty) - } else { - let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty); - self.check_type_for_ffi(cache, field_ty) - } + let field_ty = self + .cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, field_ty) + .unwrap_or(field_ty); + self.check_type_for_ffi(cache, field_ty) } /// Checks if the given `VariantDef`'s field types are "ffi-safe". @@ -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, @@ -1320,7 +1318,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if let Some(ty) = self .cx .tcx - .normalize_erasing_regions(self.cx.param_env, ty) + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty) .visit_with(&mut ProhibitOpaqueTypes) .break_value() { @@ -1338,16 +1337,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { is_static: bool, is_return_type: bool, ) { - // We have to check for opaque types before `normalize_erasing_regions`, - // which will replace opaque types with their underlying concrete type. if self.check_for_opaque_ty(sp, ty) { // We've already emitted an error due to an opaque type. return; } - // it is only OK to use this function because extern fns cannot have - // any generic types right now: - let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); + let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty); // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. So, first test that the top level isn't an array, and then @@ -1357,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; @@ -1373,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_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index f78da284a3a..a3c5ca4cda1 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -414,7 +414,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { match path { MustUsePath::Suppressed => {} MustUsePath::Boxed(path) => { - let descr_pre = &format!("{}boxed ", descr_pre); + let descr_pre = &format!("{descr_pre}boxed "); emit_must_use_untranslated( cx, path, @@ -426,7 +426,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Opaque(path) => { - let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix); + let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); emit_must_use_untranslated( cx, path, @@ -438,7 +438,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::TraitObject(path) => { - let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post); + let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); emit_must_use_untranslated( cx, path, @@ -451,7 +451,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } MustUsePath::TupleElement(elems) => { for (index, path) in elems { - let descr_post = &format!(" in tuple element {}", index); + let descr_post = &format!(" in tuple element {index}"); emit_must_use_untranslated( cx, path, @@ -464,7 +464,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } } MustUsePath::Array(path, len) => { - let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix); + let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); emit_must_use_untranslated( cx, path, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a0f2e9aed81..7e3b6e9e218 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3316,6 +3316,7 @@ declare_lint_pass! { // tidy-alphabetical-start ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_ASSOCIATED_ITEMS, + AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, ARITHMETIC_OVERFLOW, ASM_SUB_REGISTER, @@ -3400,6 +3401,7 @@ declare_lint_pass! { UNFULFILLED_LINT_EXPECTATIONS, UNINHABITED_STATIC, UNKNOWN_CRATE_TYPES, + UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_LINTS, UNNAMEABLE_TYPES, UNREACHABLE_CODE, @@ -4052,12 +4054,12 @@ declare_lint! { /// /// The compiler disables the automatic implementation if an explicit one /// exists for given type constructor. The exact rules governing this - /// are currently unsound, quite subtle, and will be modified in the future. - /// This change will cause the automatic implementation to be disabled in more + /// were previously unsound, quite subtle, and have been recently modified. + /// This change caused the automatic implementation to be disabled in more /// cases, potentially breaking some code. pub SUSPICIOUS_AUTO_TRAIT_IMPLS, Warn, - "the rules governing auto traits will change in the future", + "the rules governing auto traits have recently changed resulting in potential breakage", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, reference: "issue #93367 <https://github.com/rust-lang/rust/issues/93367>", @@ -4380,3 +4382,70 @@ declare_lint! { "effective visibility of a type is larger than the area in which it can be named", @feature_gate = sym::type_privacy_lints; } + +declare_lint! { + /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes. + /// + /// ### Example + /// + /// ```rust + /// #![feature(diagnostic_namespace)] + /// #[diagnostic::does_not_exist] + /// struct Foo; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check + /// the spelling, and check the diagnostic attribute listing for the correct name. Also + /// consider if you are using an old version of the compiler, and the attribute + /// is only available in a newer version. + pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + Warn, + "unrecognized diagnostic attribute" +} + +declare_lint! { + /// The `ambiguous_glob_imports` lint detects glob imports that should report ambiguity + /// errors, but previously didn't do that due to rustc bugs. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// + /// #![deny(ambiguous_glob_imports)] + /// pub fn foo() -> u32 { + /// use sub::*; + /// C + /// } + /// + /// mod sub { + /// mod mod1 { pub const C: u32 = 1; } + /// mod mod2 { pub const C: u32 = 2; } + /// + /// pub use mod1::*; + /// pub use mod2::*; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust compile it successfully because it + /// had lost the ambiguity error when resolve `use sub::mod2::*`. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub AMBIGUOUS_GLOB_IMPORTS, + Warn, + "detects certain glob imports that require reporting an ambiguity error", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #114095 <https://github.com/rust-lang/rust/issues/114095>", + }; +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index f6ffd46b1fe..f350957f72f 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -467,6 +467,21 @@ impl<HCX> ToStableHashKey<HCX> for LintId { } } +#[derive(Debug)] +pub struct AmbiguityErrorDiag { + pub msg: String, + pub span: Span, + pub label_span: Span, + pub label_msg: String, + pub note_msg: String, + pub b1_span: Span, + pub b1_note_msg: String, + pub b1_help_msgs: Vec<String>, + pub b2_span: Span, + pub b2_note_msg: String, + pub b2_help_msgs: Vec<String>, +} + // This could be a closure, but then implementing derive trait // becomes hacky (and it gets allocated). #[derive(Debug)] @@ -530,6 +545,9 @@ pub enum BuiltinLintDiagnostics { vis_span: Span, ident_span: Span, }, + AmbiguousGlobImports { + diag: AmbiguityErrorDiag, + }, AmbiguousGlobReexports { /// The name for which collision(s) have occurred. name: String, @@ -551,10 +569,8 @@ pub enum BuiltinLintDiagnostics { private_item_span: Span, }, UnusedQualifications { - /// The span of the unnecessarily-qualified path. - path_span: Span, - /// The replacement unqualified path. - unqualified_path: Ident, + /// The span of the unnecessarily-qualified path to remove. + removal_span: Span, }, } diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index af6f4d5eaf9..ea045462845 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -92,10 +92,8 @@ enum LLVMRustAttribute { NoCfCheck = 35, ShadowCallStack = 36, AllocSize = 37, -#if LLVM_VERSION_GE(15, 0) AllocatedPointer = 38, AllocAlign = 39, -#endif SanitizeSafeStack = 40, }; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index eb3d67e720f..ebf8a50ae8b 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1,5 +1,6 @@ #include <stdio.h> +#include <iomanip> #include <vector> #include <set> @@ -306,44 +307,55 @@ static size_t getLongestEntryLength(ArrayRef<KV> Table) { return MaxLen; } -extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, const char* TargetCPU) { +using PrintBackendInfo = void(void*, const char* Data, size_t Len); + +extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, + const char* TargetCPU, + PrintBackendInfo Print, + void* Out) { const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const Triple::ArchType HostArch = Triple(sys::getDefaultTargetTriple()).getArch(); const Triple::ArchType TargetArch = Target->getTargetTriple().getArch(); + std::ostringstream Buf; + #if LLVM_VERSION_GE(17, 0) const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getAllProcessorDescriptions(); #elif defined(LLVM_RUSTLLVM) const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getCPUTable(); #else - printf("Full target CPU help is not supported by this LLVM version.\n\n"); + Buf << "Full target CPU help is not supported by this LLVM version.\n\n"; SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} }; const ArrayRef<SubtargetSubTypeKV> CPUTable = TargetCPUKV; #endif unsigned MaxCPULen = getLongestEntryLength(CPUTable); - printf("Available CPUs for this target:\n"); + Buf << "Available CPUs for this target:\n"; // Don't print the "native" entry when the user specifies --target with a // different arch since that could be wrong or misleading. if (HostArch == TargetArch) { MaxCPULen = std::max(MaxCPULen, (unsigned) std::strlen("native")); const StringRef HostCPU = sys::getHostCPUName(); - printf(" %-*s - Select the CPU of the current host (currently %.*s).\n", - MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data()); + Buf << " " << std::left << std::setw(MaxCPULen) << "native" + << " - Select the CPU of the current host " + "(currently " << HostCPU.str() << ").\n"; } for (auto &CPU : CPUTable) { // Compare cpu against current target to label the default if (strcmp(CPU.Key, TargetCPU) == 0) { - printf(" %-*s - This is the default target CPU" - " for the current build target (currently %s).", - MaxCPULen, CPU.Key, Target->getTargetTriple().str().c_str()); + Buf << " " << std::left << std::setw(MaxCPULen) << CPU.Key + << " - This is the default target CPU for the current build target " + "(currently " << Target->getTargetTriple().str() << ")."; } else { - printf(" %-*s", MaxCPULen, CPU.Key); + Buf << " " << CPU.Key; } - printf("\n"); + Buf << "\n"; } + + const auto &BufString = Buf.str(); + Print(Out, BufString.data(), BufString.size()); } extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { @@ -789,9 +801,6 @@ LLVMRustOptimize( OptimizerLastEPCallbacks.push_back( [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; -#if LLVM_VERSION_LT(15, 0) - MPM.addPass(RequireAnalysisPass<ASanGlobalsMetadataAnalysis, Module>()); -#endif AddressSanitizerOptions opts = AddressSanitizerOptions{ CompileKernel, SanitizerOptions->SanitizeAddressRecover @@ -1354,6 +1363,11 @@ LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, if (WasmCustomSections) WasmCustomSections->eraseFromParent(); + // `llvm.ident` named metadata also gets duplicated. + auto *llvmIdent = (*MOrErr)->getNamedMetadata("llvm.ident"); + if (llvmIdent) + llvmIdent->eraseFromParent(); + return MOrErr; }; bool ClearDSOLocal = clearDSOLocalOnDeclarations(Mod, Target); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index cbc18c0f6c6..8ef39a6c866 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1,4 +1,5 @@ #include "LLVMWrapper.h" +#include "llvm/ADT/Statistic.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DiagnosticHandler.h" #include "llvm/IR/DiagnosticInfo.h" @@ -111,9 +112,26 @@ extern "C" void LLVMRustSetNormalizedTarget(LLVMModuleRef M, unwrap(M)->setTargetTriple(Triple::normalize(Triple)); } -extern "C" void LLVMRustPrintPassTimings() { - raw_fd_ostream OS(2, false); // stderr. - TimerGroup::printAll(OS); +extern "C" const char *LLVMRustPrintPassTimings(size_t *Len) { + std::string buf; + raw_string_ostream SS(buf); + TimerGroup::printAll(SS); + SS.flush(); + *Len = buf.length(); + char *CStr = (char *)malloc(*Len); + memcpy(CStr, buf.c_str(), *Len); + return CStr; +} + +extern "C" const char *LLVMRustPrintStatistics(size_t *Len) { + std::string buf; + raw_string_ostream SS(buf); + llvm::PrintStatistics(SS); + SS.flush(); + *Len = buf.length(); + char *CStr = (char *)malloc(*Len); + memcpy(CStr, buf.c_str(), *Len); + return CStr; } extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name, @@ -259,12 +277,10 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::ShadowCallStack; case AllocSize: return Attribute::AllocSize; -#if LLVM_VERSION_GE(15, 0) case AllocatedPointer: return Attribute::AllocatedPointer; case AllocAlign: return Attribute::AllocAlign; -#endif case SanitizeSafeStack: return Attribute::SafeStack; } @@ -322,20 +338,12 @@ extern "C" LLVMAttributeRef LLVMRustCreateStructRetAttr(LLVMContextRef C, LLVMTy } extern "C" LLVMAttributeRef LLVMRustCreateElementTypeAttr(LLVMContextRef C, LLVMTypeRef Ty) { -#if LLVM_VERSION_GE(15, 0) return wrap(Attribute::get(*unwrap(C), Attribute::ElementType, unwrap(Ty))); -#else - report_fatal_error("Should not be needed on LLVM < 15"); -#endif } extern "C" LLVMAttributeRef LLVMRustCreateUWTableAttr(LLVMContextRef C, bool Async) { -#if LLVM_VERSION_LT(15, 0) - return wrap(Attribute::get(*unwrap(C), Attribute::UWTable)); -#else return wrap(Attribute::getWithUWTableKind( *unwrap(C), Async ? UWTableKind::Async : UWTableKind::Sync)); -#endif } extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32_t ElementSizeArg) { @@ -348,8 +356,6 @@ extern "C" LLVMAttributeRef LLVMRustCreateAllocSizeAttr(LLVMContextRef C, uint32 )); } -#if LLVM_VERSION_GE(15, 0) - // These values **must** match ffi::AllocKindFlags. // It _happens_ to match the LLVM values of llvm::AllocFnKind, // but that's happenstance and we do explicit conversions before @@ -393,16 +399,10 @@ static llvm::AllocFnKind allocKindFromRust(LLVMRustAllocKindFlags F) { } return AFK; } -#endif extern "C" LLVMAttributeRef LLVMRustCreateAllocKindAttr(LLVMContextRef C, uint64_t AllocKindArg) { -#if LLVM_VERSION_GE(15, 0) return wrap(Attribute::get(*unwrap(C), Attribute::AllocKind, static_cast<uint64_t>(allocKindFromRust(static_cast<LLVMRustAllocKindFlags>(AllocKindArg))))); -#else - report_fatal_error( - "allockind attributes are new in LLVM 15 and should not be used on older LLVMs"); -#endif } // Simplified representation of `MemoryEffects` across the FFI boundary. @@ -499,14 +499,9 @@ LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, size_t ConstraintsLen) { -#if LLVM_VERSION_LT(15, 0) - return InlineAsm::Verify(unwrap<FunctionType>(Ty), - StringRef(Constraints, ConstraintsLen)); -#else // llvm::Error converts to true if it is an error. return !llvm::errorToBool(InlineAsm::verify( unwrap<FunctionType>(Ty), StringRef(Constraints, ConstraintsLen))); -#endif } typedef DIBuilder *LLVMRustDIBuilderRef; @@ -1631,19 +1626,11 @@ extern "C" bool LLVMRustConstInt128Get(LLVMValueRef CV, bool sext, uint64_t *hig auto C = unwrap<llvm::ConstantInt>(CV); if (C->getBitWidth() > 128) { return false; } APInt AP; -#if LLVM_VERSION_GE(15, 0) if (sext) { AP = C->getValue().sext(128); } else { AP = C->getValue().zext(128); } -#else - if (sext) { - AP = C->getValue().sextOrSelf(128); - } else { - AP = C->getValue().zextOrSelf(128); - } -#endif *low = AP.getLoBits(64).getZExtValue(); *high = AP.getHiBits(64).getZExtValue(); return true; @@ -2026,16 +2013,7 @@ extern "C" void LLVMRustGetMangledName(LLVMValueRef V, RustStringRef Str) { Mangler().getNameWithPrefix(OS, GV, true); } -// LLVMGetAggregateElement was added in LLVM 15. For earlier LLVM versions just -// use its implementation. -#if LLVM_VERSION_LT(15, 0) -extern "C" LLVMValueRef LLVMGetAggregateElement(LLVMValueRef C, unsigned Idx) { - return wrap(unwrap<Constant>(C)->getAggregateElement(Idx)); -} -#endif - extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) { -#if LLVM_VERSION_GE(15, 0) auto *CB = unwrap<CallBase>(CallSite); switch (CB->getIntrinsicID()) { case Intrinsic::arm_ldrex: @@ -2043,7 +2021,6 @@ extern "C" int32_t LLVMRustGetElementTypeArgIndex(LLVMValueRef CallSite) { case Intrinsic::arm_strex: return 1; } -#endif return -1; } diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 3cbb2c21e28..e7b80c64184 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -123,7 +123,7 @@ where return Ok(()); } let backtrace = std::backtrace::Backtrace::capture(); - writeln!(writer, "stack backtrace: \n{:?}", backtrace) + writeln!(writer, "stack backtrace: \n{backtrace:?}") } } diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index 16c4a850012..17651ce9598 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -8,7 +8,6 @@ proc-macro = true [dependencies] synstructure = "0.13.0" -# FIXME(Nilstrieb): Updating this causes changes in the diagnostics output. -syn = { version = "=2.0.8", features = ["full"] } +syn = { version = "2.0.9", features = ["full"] } proc-macro2 = "1" quote = "1" diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 8d017d149f6..f1e7b8eb6c7 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -43,7 +43,7 @@ fn decodable_body( let ty_name = s.ast().ident.to_string(); let decode_body = match s.variants() { [] => { - let message = format!("`{}` has no variants to decode", ty_name); + let message = format!("`{ty_name}` has no variants to decode"); quote! { panic!(#message) } diff --git a/compiler/rustc_metadata/src/foreign_modules.rs b/compiler/rustc_metadata/src/foreign_modules.rs index d1c2f3104d0..154eb684f11 100644 --- a/compiler/rustc_metadata/src/foreign_modules.rs +++ b/compiler/rustc_metadata/src/foreign_modules.rs @@ -1,19 +1,28 @@ +use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; use rustc_session::cstore::ForeignModule; -pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<ForeignModule> { - let mut modules = Vec::new(); +pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> FxIndexMap<DefId, ForeignModule> { + let mut modules = FxIndexMap::default(); + + // We need to collect all the `ForeignMod`, even if they are empty. for id in tcx.hir().items() { if !matches!(tcx.def_kind(id.owner_id), DefKind::ForeignMod) { continue; } + + let def_id = id.owner_id.to_def_id(); let item = tcx.hir().item(id); - if let hir::ItemKind::ForeignMod { items, .. } = item.kind { + + if let hir::ItemKind::ForeignMod { abi, items } = item.kind { let foreign_items = items.iter().map(|it| it.id.owner_id.to_def_id()).collect(); - modules.push(ForeignModule { foreign_items, def_id: id.owner_id.to_def_id() }); + modules.insert(def_id, ForeignModule { def_id, abi, foreign_items }); } } + modules } diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 238f963ed46..69a77e82f98 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -104,8 +104,8 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { }; // Load metadata back to memory: codegen may need to include it in object files. - let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir) - .unwrap_or_else(|err| { + let metadata = + EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index a1511c4b570..fb9dd660d2f 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -511,7 +511,7 @@ impl<'a> CrateLocator<'a> { rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?, dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?, }; - Ok(slot.map(|(svh, metadata)| (svh, Library { source, metadata }))) + Ok(slot.map(|(svh, metadata, _)| (svh, Library { source, metadata }))) } fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool { @@ -535,11 +535,13 @@ impl<'a> CrateLocator<'a> { // read the metadata from it if `*slot` is `None`. If the metadata couldn't // be read, it is assumed that the file isn't a valid rust library (no // errors are emitted). + // + // The `PathBuf` in `slot` will only be used for diagnostic purposes. fn extract_one( &mut self, m: FxHashMap<PathBuf, PathKind>, flavor: CrateFlavor, - slot: &mut Option<(Svh, MetadataBlob)>, + slot: &mut Option<(Svh, MetadataBlob, PathBuf)>, ) -> Result<Option<(PathBuf, PathKind)>, CrateError> { // If we are producing an rlib, and we've already loaded metadata, then // we should not attempt to discover further crate sources (unless we're @@ -550,16 +552,9 @@ impl<'a> CrateLocator<'a> { // // See also #68149 which provides more detail on why emitting the // dependency on the rlib is a bad thing. - // - // We currently do not verify that these other sources are even in sync, - // and this is arguably a bug (see #10786), but because reading metadata - // is quite slow (especially from dylibs) we currently do not read it - // from the other crate sources. if slot.is_some() { if m.is_empty() || !self.needs_crate_flavor(flavor) { return Ok(None); - } else if m.len() == 1 { - return Ok(Some(m.into_iter().next().unwrap())); } } @@ -610,8 +605,7 @@ impl<'a> CrateLocator<'a> { candidates, )); } - err_data = Some(vec![ret.as_ref().unwrap().0.clone()]); - *slot = None; + err_data = Some(vec![slot.take().unwrap().2]); } if let Some(candidates) = &mut err_data { candidates.push(lib); @@ -644,7 +638,7 @@ impl<'a> CrateLocator<'a> { continue; } } - *slot = Some((hash, metadata)); + *slot = Some((hash, metadata, lib.clone())); ret = Some((lib, kind)); } @@ -814,19 +808,26 @@ fn get_metadata_section<'p>( let compressed_len = u32::from_be_bytes(len_bytes) as usize; // Header is okay -> inflate the actual metadata - let compressed_bytes = &buf[data_start..(data_start + compressed_len)]; - debug!("inflating {} bytes of compressed metadata", compressed_bytes.len()); - // Assume the decompressed data will be at least the size of the compressed data, so we - // don't have to grow the buffer as much. - let mut inflated = Vec::with_capacity(compressed_bytes.len()); - FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated).map_err(|_| { - MetadataError::LoadFailure(format!( - "failed to decompress metadata: {}", - filename.display() - )) - })?; + let compressed_bytes = buf.slice(|buf| &buf[data_start..(data_start + compressed_len)]); + if &compressed_bytes[..cmp::min(METADATA_HEADER.len(), compressed_bytes.len())] + == METADATA_HEADER + { + // The metadata was not actually compressed. + compressed_bytes + } else { + debug!("inflating {} bytes of compressed metadata", compressed_bytes.len()); + // Assume the decompressed data will be at least the size of the compressed data, so we + // don't have to grow the buffer as much. + let mut inflated = Vec::with_capacity(compressed_bytes.len()); + FrameDecoder::new(&*compressed_bytes).read_to_end(&mut inflated).map_err(|_| { + MetadataError::LoadFailure(format!( + "failed to decompress metadata: {}", + filename.display() + )) + })?; - slice_owned(inflated, Deref::deref) + slice_owned(inflated, Deref::deref) + } } CrateFlavor::Rmeta => { // mmap the file, because only a small fraction of it is read. @@ -907,7 +908,7 @@ pub fn list_file_metadata( let flavor = get_flavor_from_path(path); match get_metadata_section(target, flavor, path, metadata_loader) { Ok(metadata) => metadata.list_crate_metadata(out), - Err(msg) => write!(out, "{}\n", msg), + Err(msg) => write!(out, "{msg}\n"), } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 9e0bf81d58d..ca5043cc263 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -1,15 +1,17 @@ use rustc_ast::{NestedMetaItem, CRATE_NODE_ID}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::{List, ParamEnv, ParamEnvAnd, Ty, TyCtxt}; use rustc_session::config::CrateType; -use rustc_session::cstore::{DllCallingConvention, DllImport, NativeLib, PeImportNameType}; +use rustc_session::cstore::{ + DllCallingConvention, DllImport, ForeignModule, NativeLib, PeImportNameType, +}; use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; use rustc_session::Session; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::{sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -66,10 +68,12 @@ fn find_bundled_library( None } -pub(crate) fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> { +pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec<NativeLib> { let mut collector = Collector { tcx, libs: Vec::new() }; - for id in tcx.hir().items() { - collector.process_item(id); + if tcx.sess.opts.unstable_opts.link_directives { + for module in tcx.foreign_modules(LOCAL_CRATE).values() { + collector.process_module(module); + } } collector.process_command_line(); collector.libs @@ -88,29 +92,20 @@ struct Collector<'tcx> { } impl<'tcx> Collector<'tcx> { - fn process_item(&mut self, id: rustc_hir::ItemId) { - if !matches!(self.tcx.def_kind(id.owner_id), DefKind::ForeignMod) { - return; - } + fn process_module(&mut self, module: &ForeignModule) { + let ForeignModule { def_id, abi, ref foreign_items } = *module; + let def_id = def_id.expect_local(); - let it = self.tcx.hir().item(id); - let hir::ItemKind::ForeignMod { abi, items: foreign_mod_items } = it.kind else { - return; - }; + let sess = self.tcx.sess; if matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic) { return; } // Process all of the #[link(..)]-style arguments - let sess = self.tcx.sess; let features = self.tcx.features(); - if !sess.opts.unstable_opts.link_directives { - return; - } - - for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) { + for m in self.tcx.get_attrs(def_id, sym::link) { let Some(items) = m.meta_item_list() else { continue; }; @@ -340,9 +335,9 @@ impl<'tcx> Collector<'tcx> { if name.as_str().contains('\0') { sess.emit_err(errors::RawDylibNoNul { span: name_span }); } - foreign_mod_items + foreign_items .iter() - .map(|child_item| { + .map(|&child_item| { self.build_dll_import( abi, import_name_type.map(|(import_name_type, _)| import_name_type), @@ -352,21 +347,12 @@ impl<'tcx> Collector<'tcx> { .collect() } _ => { - for child_item in foreign_mod_items { - if self.tcx.def_kind(child_item.id.owner_id).has_codegen_attrs() - && self - .tcx - .codegen_fn_attrs(child_item.id.owner_id) - .link_ordinal - .is_some() + for &child_item in foreign_items { + if self.tcx.def_kind(child_item).has_codegen_attrs() + && self.tcx.codegen_fn_attrs(child_item).link_ordinal.is_some() { - let link_ordinal_attr = self - .tcx - .hir() - .attrs(child_item.id.owner_id.into()) - .iter() - .find(|a| a.has_name(sym::link_ordinal)) - .unwrap(); + let link_ordinal_attr = + self.tcx.get_attr(child_item, sym::link_ordinal).unwrap(); sess.emit_err(errors::LinkOrdinalRawDylib { span: link_ordinal_attr.span, }); @@ -384,7 +370,7 @@ impl<'tcx> Collector<'tcx> { filename, kind, cfg, - foreign_module: Some(it.owner_id.to_def_id()), + foreign_module: Some(def_id.to_def_id()), verbatim, dll_imports, }); @@ -476,10 +462,10 @@ impl<'tcx> Collector<'tcx> { } } - fn i686_arg_list_size(&self, item: &hir::ForeignItemRef) -> usize { + fn i686_arg_list_size(&self, item: DefId) -> usize { let argument_types: &List<Ty<'_>> = self.tcx.erase_late_bound_regions( self.tcx - .type_of(item.id.owner_id) + .type_of(item) .instantiate_identity() .fn_sig(self.tcx) .inputs() @@ -505,8 +491,10 @@ impl<'tcx> Collector<'tcx> { &self, abi: Abi, import_name_type: Option<PeImportNameType>, - item: &hir::ForeignItemRef, + item: DefId, ) -> DllImport { + let span = self.tcx.def_span(item); + let calling_convention = if self.tcx.sess.target.arch == "x86" { match abi { Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C, @@ -520,29 +508,29 @@ impl<'tcx> Collector<'tcx> { DllCallingConvention::Vectorcall(self.i686_arg_list_size(item)) } _ => { - self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span: item.span }); + self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span }); } } } else { match abi { Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C, _ => { - self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span: item.span }); + self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span }); } } }; - let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item.id.owner_id); + let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item); let import_name_type = codegen_fn_attrs .link_ordinal .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord))); DllImport { - name: codegen_fn_attrs.link_name.unwrap_or(item.ident.name), + name: codegen_fn_attrs.link_name.unwrap_or(self.tcx.item_name(item)), import_name_type, calling_convention, - span: item.span, - is_fn: self.tcx.def_kind(item.id.owner_id).is_fn_like(), + span, + is_fn: self.tcx.def_kind(item).is_fn_like(), } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 8fa1d365728..9e67bb655d4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -34,7 +34,7 @@ use rustc_session::cstore::{ use rustc_session::Session; use rustc_span::hygiene::ExpnIndex; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{self, BytePos, ExpnId, Pos, Span, SyntaxContext, DUMMY_SP}; +use rustc_span::{self, BytePos, ExpnId, Pos, Span, SpanData, SyntaxContext, DUMMY_SP}; use proc_macro::bridge::client::ProcMacro; use std::iter::TrustedLen; @@ -513,11 +513,26 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId { impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span { fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Span { + let mode = SpanEncodingMode::decode(decoder); + let data = match mode { + SpanEncodingMode::Direct => SpanData::decode(decoder), + SpanEncodingMode::Shorthand(position) => decoder.with_position(position, |decoder| { + let mode = SpanEncodingMode::decode(decoder); + debug_assert!(matches!(mode, SpanEncodingMode::Direct)); + SpanData::decode(decoder) + }), + }; + Span::new(data.lo, data.hi, data.ctxt, data.parent) + } +} + +impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SpanData { + fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SpanData { let ctxt = SyntaxContext::decode(decoder); let tag = u8::decode(decoder); if tag == TAG_PARTIAL_SPAN { - return DUMMY_SP.with_ctxt(ctxt); + return DUMMY_SP.with_ctxt(ctxt).data(); } debug_assert!(tag == TAG_VALID_SPAN_LOCAL || tag == TAG_VALID_SPAN_FOREIGN); @@ -612,7 +627,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span { let hi = hi + source_file.translated_source_file.start_pos; // Do not try to decode parent for foreign spans. - Span::new(lo, hi, ctxt, None) + SpanData { lo, hi, ctxt, parent: None } } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 848535fb395..7dbfe0e0cb0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -246,6 +246,7 @@ provide! { tcx, def_id, other, cdata, debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy); cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index) } + assumed_wf_types_for_rpitit => { table } collect_return_position_impl_trait_in_trait_tys => { Ok(cdata .root @@ -403,10 +404,8 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) { .contains(&id) }) }, - native_libraries: |tcx, LocalCrate| native_libs::collect(tcx), - foreign_modules: |tcx, LocalCrate| { - foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect() - }, + native_libraries: native_libs::collect, + foreign_modules: foreign_modules::collect, // Returns a map from a sufficiently visible external item (i.e., an // external item that is visible from at least one local module) to a diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 2785732727f..d72053ca985 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -37,7 +37,7 @@ use rustc_session::config::{CrateType, OptLevel}; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_span::hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind}; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext}; +use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SpanData, SyntaxContext}; use std::borrow::Borrow; use std::collections::hash_map::Entry; use std::hash::Hash; @@ -53,6 +53,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { tables: TableBuilders, lazy_state: LazyState, + span_shorthands: FxHashMap<Span, usize>, type_shorthands: FxHashMap<Ty<'tcx>, usize>, predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>, @@ -177,8 +178,20 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId { impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - let span = self.data(); + match s.span_shorthands.entry(*self) { + Entry::Occupied(o) => SpanEncodingMode::Shorthand(*o.get()).encode(s), + Entry::Vacant(v) => { + let position = s.opaque.position(); + v.insert(position); + SpanEncodingMode::Direct.encode(s); + self.data().encode(s); + } + } + } +} +impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for SpanData { + fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { // Don't serialize any `SyntaxContext`s from a proc-macro crate, // since we don't load proc-macro dependencies during serialization. // This means that any hygiene information from macros used *within* @@ -213,7 +226,7 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { if s.is_proc_macro { SyntaxContext::root().encode(s); } else { - span.ctxt.encode(s); + self.ctxt.encode(s); } if self.is_dummy() { @@ -221,18 +234,18 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { } // The Span infrastructure should make sure that this invariant holds: - debug_assert!(span.lo <= span.hi); + debug_assert!(self.lo <= self.hi); - if !s.source_file_cache.0.contains(span.lo) { + if !s.source_file_cache.0.contains(self.lo) { let source_map = s.tcx.sess.source_map(); - let source_file_index = source_map.lookup_source_file_idx(span.lo); + let source_file_index = source_map.lookup_source_file_idx(self.lo); s.source_file_cache = (source_map.files()[source_file_index].clone(), source_file_index); } let (ref source_file, source_file_index) = s.source_file_cache; - debug_assert!(source_file.contains(span.lo)); + debug_assert!(source_file.contains(self.lo)); - if !source_file.contains(span.hi) { + if !source_file.contains(self.hi) { // Unfortunately, macro expansion still sometimes generates Spans // that malformed in this way. return TAG_PARTIAL_SPAN.encode(s); @@ -286,11 +299,11 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { // Encode the start position relative to the file start, so we profit more from the // variable-length integer encoding. - let lo = span.lo - source_file.start_pos; + let lo = self.lo - source_file.start_pos; // Encode length which is usually less than span.hi and profits more // from the variable-length integer encoding that we use. - let len = span.hi - span.lo; + let len = self.hi - self.lo; tag.encode(s); lo.encode(s); @@ -1131,13 +1144,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> let assoc_item = tcx.associated_item(def_id); match assoc_item.container { ty::AssocItemContainer::ImplContainer => true, - // Always encode RPITITs, since we need to be able to project - // from an RPITIT associated item to an opaque when installing - // the default projection predicates in default trait methods - // with RPITITs. - ty::AssocItemContainer::TraitContainer => { - assoc_item.defaultness(tcx).has_value() || assoc_item.is_impl_trait_in_trait() - } + ty::AssocItemContainer::TraitContainer => assoc_item.defaultness(tcx).has_value(), } } DefKind::TyParam => { @@ -1547,6 +1554,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let Some(rpitit_info) = item.opt_rpitit_info { record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); + if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) { + record_array!( + self.tables.assumed_wf_types_for_rpitit[def_id] + <- self.tcx.assumed_wf_types_for_rpitit(def_id) + ); + } } } @@ -2182,6 +2195,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) { feat: tcx.features(), tables: Default::default(), lazy_state: LazyState::NoNode, + span_shorthands: Default::default(), type_shorthands: Default::default(), predicate_shorthands: Default::default(), source_file_cache, @@ -2230,13 +2244,12 @@ pub fn provide(providers: &mut Providers) { tcx.resolutions(()) .doc_link_resolutions .get(&def_id) - .expect("no resolutions for a doc link") + .unwrap_or_else(|| span_bug!(tcx.def_span(def_id), "no resolutions for a doc link")) }, doc_link_traits_in_scope: |tcx, def_id| { - tcx.resolutions(()) - .doc_link_traits_in_scope - .get(&def_id) - .expect("no traits in scope for a doc link") + tcx.resolutions(()).doc_link_traits_in_scope.get(&def_id).unwrap_or_else(|| { + span_bug!(tcx.def_span(def_id), "no traits in scope for a doc link") + }) }, traits: |tcx, LocalCrate| { let mut traits = Vec::new(); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index f6087fbe8f6..a89e235ff28 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -51,7 +51,7 @@ mod encoder; mod table; pub(crate) fn rustc_version(cfg_version: &'static str) -> String { - format!("rustc {}", cfg_version) + format!("rustc {cfg_version}") } /// Metadata encoding version. @@ -66,6 +66,12 @@ const METADATA_VERSION: u8 = 8; /// unsigned integer, and further followed by the rustc version string. pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; +#[derive(Encodable, Decodable)] +enum SpanEncodingMode { + Shorthand(usize), + Direct, +} + /// A value of type T referred to by its absolute position /// in the metadata, and which can be decoded lazily. /// @@ -451,6 +457,7 @@ define_tables! { trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, ty::EarlyBinder<Ty<'static>>>>>, doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>, doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>, + assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>, } #[derive(TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 4c238308fe8..bb8e774cea3 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_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index bb7147ac80f..108a10b506b 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -52,6 +52,9 @@ middle_drop_check_overflow = overflow while adding drop-check rules for {$ty} .note = overflowed on {$overflow_ty} +middle_layout_references_error = + the type has an unknown layout + middle_limit_invalid = `limit` must be a non-negative integer .label = {$error_str} diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 2dc5b896993..4e242c684e3 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -380,7 +380,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId { let local_id = local_id .as_u64() .try_into() - .unwrap_or_else(|_| panic!("local id should be u32, found {:?}", local_id)); + .unwrap_or_else(|_| panic!("local id should be u32, found {local_id:?}")); Some(HirId { owner: OwnerId { def_id }, local_id: ItemLocalId::from_u32(local_id) }) } else { None diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 0ddbe7d1c29..3ad9b0d79e7 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -35,7 +35,7 @@ impl rustc_query_system::dep_graph::DepKind for DepKind { if let Some(def_id) = node.extract_def_id(tcx) { write!(f, "{}", tcx.def_path_debug_str(def_id))?; } else if let Some(ref s) = tcx.dep_graph.dep_node_debug_str(*node) { - write!(f, "{}", s)?; + write!(f, "{s}")?; } else { write!(f, "{}", node.hash)?; } diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 57b2de84b47..b346cd45391 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -132,6 +132,9 @@ pub enum LayoutError<'tcx> { #[diag(middle_cycle)] Cycle, + + #[diag(middle_layout_references_error)] + ReferencesError, } #[derive(Diagnostic)] diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 1fd68dc5cb2..0256e09e4b5 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -24,7 +24,7 @@ pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> { match node { Node::Item(Item { owner_id, - kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), + kind: ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body), .. }) | Node::TraitItem(TraitItem { @@ -534,7 +534,7 @@ impl<'hir> Map<'hir> { (m, span, hir_id) } Some(OwnerNode::Crate(item)) => (item, item.spans.inner_span, hir_id), - node => panic!("not a module: {:?}", node), + node => panic!("not a module: {node:?}"), } } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 20230217afc..9ecc7580f5c 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -218,14 +218,12 @@ pub fn explain_lint_level_source( let hyphen_case_lint_name = name.replace('_', "-"); if lint_flag_val.as_str() == name { err.note_once(format!( - "requested on the command line with `{} {}`", - flag, hyphen_case_lint_name + "requested on the command line with `{flag} {hyphen_case_lint_name}`" )); } else { let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-"); err.note_once(format!( - "`{} {}` implied by `{} {}`", - flag, hyphen_case_lint_name, flag, hyphen_case_flag_val + "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`" )); } } @@ -237,8 +235,7 @@ pub fn explain_lint_level_source( if lint_attr_name.as_str() != name { let level_str = level.as_str(); err.note_once(format!( - "`#[{}({})]` implied by `#[{}({})]`", - level_str, name, level_str, lint_attr_name + "`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`" )); } } @@ -278,6 +275,7 @@ pub fn explain_lint_level_source( /// // ^^^^^^^^^^^^^^^^^^^^^ returns `&mut DiagnosticBuilder` by default /// ) /// ``` +#[track_caller] pub fn struct_lint_level( sess: &Session, lint: &'static Lint, @@ -291,6 +289,7 @@ pub fn struct_lint_level( ) { // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to // the "real" work. + #[track_caller] fn struct_lint_level_impl( sess: &Session, lint: &'static Lint, @@ -414,12 +413,11 @@ pub fn struct_lint_level( FutureIncompatibilityReason::EditionError(edition) => { let current_edition = sess.edition(); format!( - "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!", - current_edition, edition + "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!" ) } FutureIncompatibilityReason::EditionSemanticsChange(edition) => { - format!("this changes meaning in Rust {}", edition) + format!("this changes meaning in Rust {edition}") } FutureIncompatibilityReason::Custom(reason) => reason.to_owned(), }; diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index cd1c6c330bc..fca16d8e509 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -43,7 +43,7 @@ macro_rules! span_bug { #[macro_export] macro_rules! CloneLiftImpls { - ($($ty:ty,)+) => { + ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::Lift<'tcx> for $ty { type Lifted = Self; @@ -59,7 +59,7 @@ macro_rules! CloneLiftImpls { /// allocated data** (i.e., don't need to be folded). #[macro_export] macro_rules! TrivialTypeTraversalImpls { - ($($ty:ty,)+) => { + ($($ty:ty),+ $(,)?) => { $( impl<'tcx> $crate::ty::fold::TypeFoldable<$crate::ty::TyCtxt<'tcx>> for $ty { fn try_fold_with<F: $crate::ty::fold::FallibleTypeFolder<$crate::ty::TyCtxt<'tcx>>>( diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index c4601a1fb41..02fd6ed7ba6 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -100,6 +100,8 @@ bitflags! { const REALLOCATOR = 1 << 18; /// `#[rustc_allocator_zeroed]`: a hint to LLVM that the function only allocates zeroed memory. const ALLOCATOR_ZEROED = 1 << 19; + /// `#[no_builtins]`: indicates that disable implicit builtin knowledge of functions for the function. + const NO_BUILTINS = 1 << 20; } } diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 5baeb1ee0cf..1913421f54c 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -178,7 +178,12 @@ impl EffectiveVisibilities { // All effective visibilities except `reachable_through_impl_trait` are limited to // nominal visibility. For some items nominal visibility doesn't make sense so we // don't check this condition for them. - if !matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { + let is_impl = matches!(tcx.def_kind(def_id), DefKind::Impl { .. }); + let is_associated_item_in_trait_impl = tcx + .impl_of_method(def_id.to_def_id()) + .and_then(|impl_id| tcx.trait_id_of_impl(impl_id)) + .is_some(); + if !is_impl && !is_associated_item_in_trait_impl { let nominal_vis = tcx.visibility(def_id); if !nominal_vis.is_at_least(ev.reachable, tcx) { span_bug!( @@ -186,7 +191,7 @@ impl EffectiveVisibilities { "{:?}: reachable {:?} > nominal {:?}", def_id, ev.reachable, - nominal_vis + nominal_vis, ); } } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 60844c17e47..908ab8b613e 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -107,7 +107,7 @@ pub fn report_unstable( soft_handler: impl FnOnce(&'static Lint, Span, String), ) { let msg = match reason { - Some(r) => format!("use of unstable library feature '{}': {}", feature, r), + Some(r) => format!("use of unstable library feature '{feature}': {r}"), None => format!("use of unstable library feature '{}'", &feature), }; @@ -170,7 +170,7 @@ pub fn deprecation_suggestion( if let Some(suggestion) = suggestion { diag.span_suggestion_verbose( span, - format!("replace the use of the deprecated {}", kind), + format!("replace the use of the deprecated {kind}"), suggestion, Applicability::MachineApplicable, ); @@ -189,12 +189,12 @@ fn deprecation_message( path: &str, ) -> String { let message = if is_in_effect { - format!("use of deprecated {} `{}`", kind, path) + format!("use of deprecated {kind} `{path}`") } else { let since = since.as_ref().map(Symbol::as_str); if since == Some("TBD") { - format!("use of {} `{}` that will be deprecated in a future Rust version", kind, path) + format!("use of {kind} `{path}` that will be deprecated in a future Rust version") } else { format!( "use of {} `{}` that will be deprecated in future version {}", @@ -206,7 +206,7 @@ fn deprecation_message( }; match note { - Some(reason) => format!("{}: {}", message, reason), + Some(reason) => format!("{message}: {reason}"), None => message, } } @@ -312,7 +312,7 @@ fn suggestion_for_allocator_api( return Some(( inner_types, "consider wrapping the inner types in tuple".to_string(), - format!("({})", snippet), + format!("({snippet})"), Applicability::MaybeIncorrect, )); } @@ -599,7 +599,7 @@ impl<'tcx> TyCtxt<'tcx> { |span, def_id| { // The API could be uncallable for other reasons, for example when a private module // was referenced. - self.sess.delay_span_bug(span, format!("encountered unmarked API: {:?}", def_id)); + self.sess.delay_span_bug(span, format!("encountered unmarked API: {def_id:?}")); }, ) } diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs index 7722e7b47cf..0ad17e819c7 100644 --- a/compiler/rustc_middle/src/mir/basic_blocks.rs +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -178,9 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { } } -TrivialTypeTraversalAndLiftImpls! { - Cache, -} +TrivialTypeTraversalAndLiftImpls! { Cache } impl<S: Encoder> Encodable<S> for Cache { #[inline] diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index db24dae1130..d7d6e3a0086 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -6,69 +6,43 @@ use rustc_span::Symbol; use std::fmt::{self, Debug, Formatter}; rustc_index::newtype_index! { - /// An ExpressionOperandId value is assigned directly from either a - /// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32() - /// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a - /// constant value of `0`. - #[derive(HashStable)] - #[max = 0xFFFF_FFFF] - #[debug_format = "ExpressionOperandId({})"] - pub struct ExpressionOperandId { - } -} - -impl ExpressionOperandId { - /// An expression operand for a "zero counter", as described in the following references: + /// ID of a coverage counter. Values ascend from 0. /// - /// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter> - /// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#tag> - /// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter-expressions> - /// - /// This operand can be used to count two or more separate code regions with a single counter, - /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for - /// one of the code regions, and inserting `CounterExpression`s ("add ZERO to the counter") in - /// the coverage map for the other code regions. - pub const ZERO: Self = Self::from_u32(0); -} - -rustc_index::newtype_index! { + /// Note that LLVM handles counter IDs as `uint32_t`, so there is no need + /// to use a larger representation on the Rust side. #[derive(HashStable)] #[max = 0xFFFF_FFFF] - #[debug_format = "CounterValueReference({})"] - pub struct CounterValueReference {} + #[debug_format = "CounterId({})"] + pub struct CounterId {} } -impl CounterValueReference { - /// Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO. - pub const START: Self = Self::from_u32(1); +impl CounterId { + pub const START: Self = Self::from_u32(0); - /// Returns explicitly-requested zero-based version of the counter id, used - /// during codegen. LLVM expects zero-based indexes. - pub fn zero_based_index(self) -> u32 { - let one_based_index = self.as_u32(); - debug_assert!(one_based_index > 0); - one_based_index - 1 + #[inline(always)] + pub fn next_id(self) -> Self { + Self::from_u32(self.as_u32() + 1) } } rustc_index::newtype_index! { - /// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32() + /// ID of a coverage-counter expression. Values ascend from 0. /// - /// Values descend from u32::MAX. + /// Note that LLVM handles expression IDs as `uint32_t`, so there is no need + /// to use a larger representation on the Rust side. #[derive(HashStable)] #[max = 0xFFFF_FFFF] - #[debug_format = "InjectedExpressionId({})"] - pub struct InjectedExpressionId {} + #[debug_format = "ExpressionId({})"] + pub struct ExpressionId {} } -rustc_index::newtype_index! { - /// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32() - /// - /// Values ascend from 0. - #[derive(HashStable)] - #[max = 0xFFFF_FFFF] - #[debug_format = "InjectedExpressionIndex({})"] - pub struct InjectedExpressionIndex {} +impl ExpressionId { + pub const START: Self = Self::from_u32(0); + + #[inline(always)] + pub fn next_id(self) -> Self { + Self::from_u32(self.as_u32() + 1) + } } rustc_index::newtype_index! { @@ -81,17 +55,25 @@ rustc_index::newtype_index! { pub struct MappedExpressionIndex {} } -impl From<CounterValueReference> for ExpressionOperandId { - #[inline] - fn from(v: CounterValueReference) -> ExpressionOperandId { - ExpressionOperandId::from(v.as_u32()) - } +/// Operand of a coverage-counter expression. +/// +/// Operands can be a constant zero value, an actual coverage counter, or another +/// expression. Counter/expression operands are referred to by ID. +#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub enum Operand { + Zero, + Counter(CounterId), + Expression(ExpressionId), } -impl From<InjectedExpressionId> for ExpressionOperandId { - #[inline] - fn from(v: InjectedExpressionId) -> ExpressionOperandId { - ExpressionOperandId::from(v.as_u32()) +impl Debug for Operand { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Zero => write!(f, "Zero"), + Self::Counter(id) => f.debug_tuple("Counter").field(&id.as_u32()).finish(), + Self::Expression(id) => f.debug_tuple("Expression").field(&id.as_u32()).finish(), + } } } @@ -99,23 +81,27 @@ impl From<InjectedExpressionId> for ExpressionOperandId { pub enum CoverageKind { Counter { function_source_hash: u64, - id: CounterValueReference, + /// ID of this counter within its enclosing function. + /// Expressions in the same function can refer to it as an operand. + id: CounterId, }, Expression { - id: InjectedExpressionId, - lhs: ExpressionOperandId, + /// ID of this coverage-counter expression within its enclosing function. + /// Other expressions in the same function can refer to it as an operand. + id: ExpressionId, + lhs: Operand, op: Op, - rhs: ExpressionOperandId, + rhs: Operand, }, Unreachable, } impl CoverageKind { - pub fn as_operand_id(&self) -> ExpressionOperandId { + pub fn as_operand(&self) -> Operand { use CoverageKind::*; match *self { - Counter { id, .. } => ExpressionOperandId::from(id), - Expression { id, .. } => ExpressionOperandId::from(id), + Counter { id, .. } => Operand::Counter(id), + Expression { id, .. } => Operand::Expression(id), Unreachable => bug!("Unreachable coverage cannot be part of an expression"), } } @@ -132,14 +118,14 @@ impl Debug for CoverageKind { Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()), Expression { id, lhs, op, rhs } => write!( fmt, - "Expression({:?}) = {} {} {}", + "Expression({:?}) = {:?} {} {:?}", id.index(), - lhs.index(), + lhs, match op { Op::Add => "+", Op::Subtract => "-", }, - rhs.index(), + rhs, ), Unreachable => write!(fmt, "Unreachable"), } diff --git a/compiler/rustc_middle/src/mir/generic_graph.rs b/compiler/rustc_middle/src/mir/generic_graph.rs index d1f3561c02c..d1753427e74 100644 --- a/compiler/rustc_middle/src/mir/generic_graph.rs +++ b/compiler/rustc_middle/src/mir/generic_graph.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::TyCtxt; pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Graph { let def_id = body.source.def_id(); let def_name = graphviz_safe_def_name(def_id); - let graph_name = format!("Mir_{}", def_name); + let graph_name = format!("Mir_{def_name}"); let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode; // Nodes @@ -48,7 +48,7 @@ fn bb_to_graph_node(block: BasicBlock, body: &Body<'_>, dark_mode: bool) -> Node }; let style = NodeStyle { title_bg: Some(bgcolor.to_owned()), ..Default::default() }; - let mut stmts: Vec<String> = data.statements.iter().map(|x| format!("{:?}", x)).collect(); + let mut stmts: Vec<String> = data.statements.iter().map(|x| format!("{x:?}")).collect(); // add the terminator to the stmts, gsgdt can print it out separately let mut terminator_head = String::new(); diff --git a/compiler/rustc_middle/src/mir/generic_graphviz.rs b/compiler/rustc_middle/src/mir/generic_graphviz.rs index ccae7e159b1..299b50525cb 100644 --- a/compiler/rustc_middle/src/mir/generic_graphviz.rs +++ b/compiler/rustc_middle/src/mir/generic_graphviz.rs @@ -70,8 +70,8 @@ impl< writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; let content_attrs_str = content_attrs.join(" "); - writeln!(w, r#" node [{}];"#, content_attrs_str)?; - writeln!(w, r#" edge [{}];"#, content_attrs_str)?; + writeln!(w, r#" node [{content_attrs_str}];"#)?; + writeln!(w, r#" edge [{content_attrs_str}];"#)?; // Graph label if let Some(graph_label) = &self.graph_label { @@ -112,7 +112,7 @@ impl< // (format!("{:?}", node), color) // }; let color = if dark_mode { "dimgray" } else { "gray" }; - let (blk, bgcolor) = (format!("{:?}", node), color); + let (blk, bgcolor) = (format!("{node:?}"), color); write!( w, r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#, @@ -151,7 +151,7 @@ impl< } else { "".to_owned() }; - writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?; + writeln!(w, r#" {src} -> {trg} [label=<{escaped_edge_label}>];"#)?; } Ok(()) } @@ -163,7 +163,7 @@ impl< W: Write, { let escaped_label = dot::escape_html(label); - writeln!(w, r#" label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label) + writeln!(w, r#" label=<<br/><br/>{escaped_label}<br align="left"/><br/><br/><br/>>;"#) } fn node(&self, node: G::Node) -> String { diff --git a/compiler/rustc_middle/src/mir/graphviz.rs b/compiler/rustc_middle/src/mir/graphviz.rs index 2de73db3a3c..5c7de864430 100644 --- a/compiler/rustc_middle/src/mir/graphviz.rs +++ b/compiler/rustc_middle/src/mir/graphviz.rs @@ -127,5 +127,5 @@ fn write_graph_label<'tcx, W: std::fmt::Write>( } fn escape<T: Debug>(t: &T) -> String { - dot::escape_html(&format!("{:?}", t)) + dot::escape_html(&format!("{t:?}")) } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index b8030d9db13..c1cb2f2e497 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -571,7 +571,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> assert!(self.mutability == Mutability::Mut); // `to_bits_or_ptr_internal` is the right method because we just want to store this data - // as-is into memory. + // as-is into memory. This also double-checks that `val.size()` matches `range.size`. let (bytes, provenance) = match val.to_bits_or_ptr_internal(range.size)? { Right(ptr) => { let (provenance, offset) = ptr.into_parts(); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs index d4dd56a42c1..2c6bb908f39 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/init_mask.rs @@ -542,11 +542,7 @@ impl InitMaskMaterialized { debug_assert_eq!( result, find_bit_slow(self, start, end, is_init), - "optimized implementation of find_bit is wrong for start={:?} end={:?} is_init={} init_mask={:#?}", - start, - end, - is_init, - self + "optimized implementation of find_bit is wrong for start={start:?} end={end:?} is_init={is_init} init_mask={self:#?}" ); result diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 372452ea29a..d44dfa2172a 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -12,7 +12,8 @@ use rustc_errors::{ use rustc_macros::HashStable; use rustc_session::CtfeBacktrace; use rustc_span::def_id::DefId; -use rustc_target::abi::{call, Align, Size, WrappingRange}; +use rustc_target::abi::{call, Align, Size, VariantIdx, WrappingRange}; + use std::borrow::Cow; use std::{any::Any, backtrace::Backtrace, fmt}; @@ -66,9 +67,7 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo { } } -TrivialTypeTraversalAndLiftImpls! { - ErrorHandled, -} +TrivialTypeTraversalAndLiftImpls! { ErrorHandled } pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>; pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>; @@ -156,7 +155,7 @@ impl<'tcx> InterpErrorInfo<'tcx> { } fn print_backtrace(backtrace: &Backtrace) { - eprintln!("\n\nAn error occurred in miri:\n{}", backtrace); + eprintln!("\n\nAn error occurred in miri:\n{backtrace}"); } impl From<ErrorGuaranteed> for InterpErrorInfo<'_> { @@ -191,9 +190,8 @@ pub enum InvalidProgramInfo<'tcx> { FnAbiAdjustForForeignAbi(call::AdjustForForeignAbiError), /// SizeOf of unsized type was requested. SizeOfUnsizedType(Ty<'tcx>), - /// An unsized local was accessed without having been initialized. - /// This is not meaningful as we can't even have backing memory for such locals. - UninitUnsizedLocal, + /// We are runnning into a nonsense situation due to ConstProp violating our invariants. + ConstPropNonsense, } /// Details of why a pointer had to be in-bounds. @@ -324,7 +322,9 @@ pub enum UndefinedBehaviorInfo<'a> { /// Data size is not equal to target size. ScalarSizeMismatch(ScalarSizeMismatch), /// A discriminant of an uninhabited enum variant is written. - UninhabitedEnumVariantWritten, + UninhabitedEnumVariantWritten(VariantIdx), + /// An uninhabited enum variant is projected. + UninhabitedEnumVariantRead(VariantIdx), /// Validation error. Validation(ValidationErrorInfo<'a>), // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically @@ -394,6 +394,7 @@ pub enum ValidationErrorKind<'tcx> { UnsafeCell, UninhabitedVal { ty: Ty<'tcx> }, InvalidEnumTag { value: String }, + UninhabitedEnumTag, UninitEnumTag, UninitStr, Uninit { expected: ExpectedKind }, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 69c15e9cc06..8b5a8d17301 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -176,7 +176,7 @@ impl<'tcx> GlobalId<'tcx> { pub fn display(self, tcx: TyCtxt<'tcx>) -> String { let instance_name = with_no_trimmed_paths!(tcx.def_path_str(self.instance.def.def_id())); if let Some(promoted) = self.promoted { - format!("{}::{:?}", instance_name, promoted) + format!("{instance_name}::{promoted:?}") } else { instance_name } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index c9db0e7c11d..fc659ce18a4 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -139,7 +139,6 @@ impl<'tcx> TyCtxt<'tcx> { cid: GlobalId<'tcx>, span: Option<Span>, ) -> EvalToConstValueResult<'tcx> { - let param_env = param_env.with_const(); // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.and(cid)); @@ -158,8 +157,6 @@ impl<'tcx> TyCtxt<'tcx> { cid: GlobalId<'tcx>, span: Option<Span>, ) -> EvalToValTreeResult<'tcx> { - let param_env = param_env.with_const(); - debug!(?param_env); // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.erase_regions(param_env.and(cid)); @@ -204,7 +201,6 @@ impl<'tcx> TyCtxtAt<'tcx> { gid: GlobalId<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Result<mir::ConstAllocation<'tcx>, ErrorHandled> { - let param_env = param_env.with_const(); trace!("eval_to_allocation: Need to compute {:?}", gid); let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?; Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory()) @@ -224,8 +220,7 @@ impl<'tcx> TyCtxtEnsure<'tcx> { let args = GenericArgs::identity_for_item(self.tcx, def_id); let instance = ty::Instance::new(def_id, args); let cid = GlobalId { instance, promoted: None }; - let param_env = - self.tcx.param_env(def_id).with_reveal_all_normalized(self.tcx).with_const(); + let param_env = self.tcx.param_env(def_id).with_reveal_all_normalized(self.tcx); // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. let inputs = self.tcx.erase_regions(param_env.and(cid)); @@ -238,7 +233,7 @@ impl<'tcx> TyCtxtEnsure<'tcx> { assert!(self.tcx.is_static(def_id)); let instance = ty::Instance::mono(self.tcx, def_id); let gid = GlobalId { instance, promoted: None }; - let param_env = ty::ParamEnv::reveal_all().with_const(); + let param_env = ty::ParamEnv::reveal_all(); trace!("eval_to_allocation: Need to compute {:?}", gid); self.eval_to_allocation_raw(param_env.and(gid)) } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 0416411dfe1..20861d5ffa4 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -135,8 +135,8 @@ static_assert_size!(Scalar, 24); impl<Prov: Provenance> fmt::Debug for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(ptr, _size) => write!(f, "{:?}", ptr), - Scalar::Int(int) => write!(f, "{:?}", int), + Scalar::Ptr(ptr, _size) => write!(f, "{ptr:?}"), + Scalar::Int(int) => write!(f, "{int:?}"), } } } @@ -144,8 +144,8 @@ impl<Prov: Provenance> fmt::Debug for Scalar<Prov> { impl<Prov: Provenance> fmt::Display for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), - Scalar::Int(int) => write!(f, "{}", int), + Scalar::Ptr(ptr, _size) => write!(f, "pointer to {ptr:?}"), + Scalar::Int(int) => write!(f, "{int}"), } } } @@ -153,8 +153,8 @@ impl<Prov: Provenance> fmt::Display for Scalar<Prov> { impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Scalar::Ptr(ptr, _size) => write!(f, "pointer to {:?}", ptr), - Scalar::Int(int) => write!(f, "{:#x}", int), + Scalar::Ptr(ptr, _size) => write!(f, "pointer to {ptr:?}"), + Scalar::Int(int) => write!(f, "{int:#x}"), } } } @@ -320,6 +320,14 @@ impl<Prov> Scalar<Prov> { } }) } + + #[inline] + pub fn size(self) -> Size { + match self { + Scalar::Int(int) => int.size(), + Scalar::Ptr(_ptr, sz) => Size::from_bytes(sz), + } + } } impl<'tcx, Prov: Provenance> Scalar<Prov> { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 97f53a59fd6..c1f87d79b83 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -619,7 +619,7 @@ impl<D: TyDecoder, T: Decodable<D>> Decodable<D> for ClearCrossCrate<T> { let val = T::decode(d); ClearCrossCrate::Set(val) } - tag => panic!("Invalid tag for ClearCrossCrate: {:?}", tag), + tag => panic!("Invalid tag for ClearCrossCrate: {tag:?}"), } } } @@ -706,9 +706,7 @@ pub enum BindingForm<'tcx> { RefForGuard, } -TrivialTypeTraversalAndLiftImpls! { - BindingForm<'tcx>, -} +TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> } mod binding_form_impl { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -1048,12 +1046,12 @@ pub enum VarDebugInfoContents<'tcx> { impl<'tcx> Debug for VarDebugInfoContents<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { match self { - VarDebugInfoContents::Const(c) => write!(fmt, "{}", c), - VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p), + VarDebugInfoContents::Const(c) => write!(fmt, "{c}"), + VarDebugInfoContents::Place(p) => write!(fmt, "{p:?}"), VarDebugInfoContents::Composite { ty, fragments } => { - write!(fmt, "{:?}{{ ", ty)?; + write!(fmt, "{ty:?}{{ ")?; for f in fragments.iter() { - write!(fmt, "{:?}, ", f)?; + write!(fmt, "{f:?}, ")?; } write!(fmt, "}}") } @@ -1317,55 +1315,47 @@ impl<O> AssertKind<O> { match self { BoundsCheck { ref len, ref index } => write!( f, - "\"index out of bounds: the length is {{}} but the index is {{}}\", {:?}, {:?}", - len, index + "\"index out of bounds: the length is {{}} but the index is {{}}\", {len:?}, {index:?}" ), OverflowNeg(op) => { - write!(f, "\"attempt to negate `{{}}`, which would overflow\", {:?}", op) + write!(f, "\"attempt to negate `{{}}`, which would overflow\", {op:?}") } - DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {:?}", op), + DivisionByZero(op) => write!(f, "\"attempt to divide `{{}}` by zero\", {op:?}"), RemainderByZero(op) => write!( f, - "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {:?}", - op + "\"attempt to calculate the remainder of `{{}}` with a divisor of zero\", {op:?}" ), Overflow(BinOp::Add, l, r) => write!( f, - "\"attempt to compute `{{}} + {{}}`, which would overflow\", {:?}, {:?}", - l, r + "\"attempt to compute `{{}} + {{}}`, which would overflow\", {l:?}, {r:?}" ), Overflow(BinOp::Sub, l, r) => write!( f, - "\"attempt to compute `{{}} - {{}}`, which would overflow\", {:?}, {:?}", - l, r + "\"attempt to compute `{{}} - {{}}`, which would overflow\", {l:?}, {r:?}" ), Overflow(BinOp::Mul, l, r) => write!( f, - "\"attempt to compute `{{}} * {{}}`, which would overflow\", {:?}, {:?}", - l, r + "\"attempt to compute `{{}} * {{}}`, which would overflow\", {l:?}, {r:?}" ), Overflow(BinOp::Div, l, r) => write!( f, - "\"attempt to compute `{{}} / {{}}`, which would overflow\", {:?}, {:?}", - l, r + "\"attempt to compute `{{}} / {{}}`, which would overflow\", {l:?}, {r:?}" ), Overflow(BinOp::Rem, l, r) => write!( f, - "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {:?}, {:?}", - l, r + "\"attempt to compute the remainder of `{{}} % {{}}`, which would overflow\", {l:?}, {r:?}" ), Overflow(BinOp::Shr, _, r) => { - write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {:?}", r) + write!(f, "\"attempt to shift right by `{{}}`, which would overflow\", {r:?}") } Overflow(BinOp::Shl, _, r) => { - write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {:?}", r) + write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}") } MisalignedPointerDereference { required, found } => { write!( f, - "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {:?}, {:?}", - required, found + "\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}" ) } _ => write!(f, "\"{}\"", self.description()), @@ -1461,9 +1451,9 @@ impl Debug for Statement<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::StatementKind::*; match self.kind { - Assign(box (ref place, ref rv)) => write!(fmt, "{:?} = {:?}", place, rv), + Assign(box (ref place, ref rv)) => write!(fmt, "{place:?} = {rv:?}"), FakeRead(box (ref cause, ref place)) => { - write!(fmt, "FakeRead({:?}, {:?})", cause, place) + write!(fmt, "FakeRead({cause:?}, {place:?})") } Retag(ref kind, ref place) => write!( fmt, @@ -1476,20 +1466,20 @@ impl Debug for Statement<'_> { }, place, ), - StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place), - StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place), + StorageLive(ref place) => write!(fmt, "StorageLive({place:?})"), + StorageDead(ref place) => write!(fmt, "StorageDead({place:?})"), SetDiscriminant { ref place, variant_index } => { - write!(fmt, "discriminant({:?}) = {:?}", place, variant_index) + write!(fmt, "discriminant({place:?}) = {variant_index:?}") } - Deinit(ref place) => write!(fmt, "Deinit({:?})", place), + Deinit(ref place) => write!(fmt, "Deinit({place:?})"), PlaceMention(ref place) => { - write!(fmt, "PlaceMention({:?})", place) + write!(fmt, "PlaceMention({place:?})") } AscribeUserType(box (ref place, ref c_ty), ref variance) => { - write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) + write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})") } Coverage(box self::Coverage { ref kind, code_region: Some(ref rgn) }) => { - write!(fmt, "Coverage::{:?} for {:?}", kind, rgn) + write!(fmt, "Coverage::{kind:?} for {rgn:?}") } Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind), Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"), @@ -1769,13 +1759,13 @@ impl Debug for Place<'_> { for elem in self.projection.iter() { match elem { ProjectionElem::OpaqueCast(ty) => { - write!(fmt, " as {})", ty)?; + write!(fmt, " as {ty})")?; } ProjectionElem::Downcast(Some(name), _index) => { - write!(fmt, " as {})", name)?; + write!(fmt, " as {name})")?; } ProjectionElem::Downcast(None, index) => { - write!(fmt, " as variant#{:?})", index)?; + write!(fmt, " as variant#{index:?})")?; } ProjectionElem::Deref => { write!(fmt, ")")?; @@ -1784,25 +1774,25 @@ impl Debug for Place<'_> { write!(fmt, ".{:?}: {:?})", field.index(), ty)?; } ProjectionElem::Index(ref index) => { - write!(fmt, "[{:?}]", index)?; + write!(fmt, "[{index:?}]")?; } ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => { - write!(fmt, "[{:?} of {:?}]", offset, min_length)?; + write!(fmt, "[{offset:?} of {min_length:?}]")?; } ProjectionElem::ConstantIndex { offset, min_length, from_end: true } => { - write!(fmt, "[-{:?} of {:?}]", offset, min_length)?; + write!(fmt, "[-{offset:?} of {min_length:?}]")?; } ProjectionElem::Subslice { from, to, from_end: true } if to == 0 => { - write!(fmt, "[{:?}:]", from)?; + write!(fmt, "[{from:?}:]")?; } ProjectionElem::Subslice { from, to, from_end: true } if from == 0 => { - write!(fmt, "[:-{:?}]", to)?; + write!(fmt, "[:-{to:?}]")?; } ProjectionElem::Subslice { from, to, from_end: true } => { - write!(fmt, "[{:?}:-{:?}]", from, to)?; + write!(fmt, "[{from:?}:-{to:?}]")?; } ProjectionElem::Subslice { from, to, from_end: false } => { - write!(fmt, "[{:?}..{:?}]", from, to)?; + write!(fmt, "[{from:?}..{to:?}]")?; } } } @@ -1896,9 +1886,9 @@ impl<'tcx> Debug for Operand<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use self::Operand::*; match *self { - Constant(ref a) => write!(fmt, "{:?}", a), - Copy(ref place) => write!(fmt, "{:?}", place), - Move(ref place) => write!(fmt, "move {:?}", place), + Constant(ref a) => write!(fmt, "{a:?}"), + Copy(ref place) => write!(fmt, "{place:?}"), + Move(ref place) => write!(fmt, "move {place:?}"), } } } @@ -1937,11 +1927,11 @@ impl<'tcx> Operand<'tcx> { let param_env_and_ty = ty::ParamEnv::empty().and(ty); let type_size = tcx .layout_of(param_env_and_ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}")) .size; let scalar_size = match val { Scalar::Int(int) => int.size(), - _ => panic!("Invalid scalar type {:?}", val), + _ => panic!("Invalid scalar type {val:?}"), }; scalar_size == type_size }); @@ -2057,26 +2047,26 @@ impl<'tcx> Debug for Rvalue<'tcx> { use self::Rvalue::*; match *self { - Use(ref place) => write!(fmt, "{:?}", place), + Use(ref place) => write!(fmt, "{place:?}"), Repeat(ref a, b) => { - write!(fmt, "[{:?}; ", a)?; + write!(fmt, "[{a:?}; ")?; pretty_print_const(b, fmt, false)?; write!(fmt, "]") } - Len(ref a) => write!(fmt, "Len({:?})", a), + Len(ref a) => write!(fmt, "Len({a:?})"), Cast(ref kind, ref place, ref ty) => { - write!(fmt, "{:?} as {:?} ({:?})", place, ty, kind) + write!(fmt, "{place:?} as {ty:?} ({kind:?})") } - BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{:?}({:?}, {:?})", op, a, b), + BinaryOp(ref op, box (ref a, ref b)) => write!(fmt, "{op:?}({a:?}, {b:?})"), CheckedBinaryOp(ref op, box (ref a, ref b)) => { - write!(fmt, "Checked{:?}({:?}, {:?})", op, a, b) + write!(fmt, "Checked{op:?}({a:?}, {b:?})") } - UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a), - Discriminant(ref place) => write!(fmt, "discriminant({:?})", place), + UnaryOp(ref op, ref a) => write!(fmt, "{op:?}({a:?})"), + Discriminant(ref place) => write!(fmt, "discriminant({place:?})"), NullaryOp(ref op, ref t) => match op { - NullOp::SizeOf => write!(fmt, "SizeOf({:?})", t), - NullOp::AlignOf => write!(fmt, "AlignOf({:?})", t), - NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({:?}, {:?})", t, fields), + NullOp::SizeOf => write!(fmt, "SizeOf({t:?})"), + NullOp::AlignOf => write!(fmt, "AlignOf({t:?})"), + NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t:?}, {fields:?})"), }, ThreadLocalRef(did) => ty::tls::with(|tcx| { let muta = tcx.static_mutability(did).unwrap().prefix_str(); @@ -2103,10 +2093,10 @@ impl<'tcx> Debug for Rvalue<'tcx> { // Do not even print 'static String::new() }; - write!(fmt, "&{}{}{:?}", region, kind_str, place) + write!(fmt, "&{region}{kind_str}{place:?}") } - CopyForDeref(ref place) => write!(fmt, "deref_copy {:#?}", place), + CopyForDeref(ref place) => write!(fmt, "deref_copy {place:#?}"), AddressOf(mutability, ref place) => { let kind_str = match mutability { @@ -2114,7 +2104,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { Mutability::Not => "const", }; - write!(fmt, "&raw {} {:?}", kind_str, place) + write!(fmt, "&raw {kind_str} {place:?}") } Aggregate(ref kind, ref places) => { @@ -2127,7 +2117,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { }; match **kind { - AggregateKind::Array(_) => write!(fmt, "{:?}", places), + AggregateKind::Array(_) => write!(fmt, "{places:?}"), AggregateKind::Tuple => { if places.is_empty() { @@ -2213,7 +2203,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { } ShallowInitBox(ref place, ref ty) => { - write!(fmt, "ShallowInitBox({:?}, {:?})", place, ty) + write!(fmt, "ShallowInitBox({place:?}, {ty:?})") } } } @@ -2757,7 +2747,7 @@ rustc_index::newtype_index! { impl<'tcx> Debug for Constant<'tcx> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!(fmt, "{}", self) + write!(fmt, "{self}") } } @@ -2833,7 +2823,7 @@ fn pretty_print_const_value<'tcx>( let ty = tcx.lift(ty).unwrap(); if tcx.sess.verbose() { - fmt.write_str(&format!("ConstValue({:?}: {})", ct, ty))?; + fmt.write_str(&format!("ConstValue({ct:?}: {ty})"))?; return Ok(()); } @@ -2903,7 +2893,7 @@ fn pretty_print_const_value<'tcx>( fmt.write_str(")")?; } ty::Adt(def, _) if def.variants().is_empty() => { - fmt.write_str(&format!("{{unreachable(): {}}}", ty))?; + fmt.write_str(&format!("{{unreachable(): {ty}}}"))?; } ty::Adt(def, args) => { let variant_idx = contents diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 16addc2dc1e..ddb6cc15bc1 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -56,6 +56,8 @@ impl<'tcx> MonoItem<'tcx> { } } + // Note: if you change how item size estimates work, you might need to + // change NON_INCR_MIN_CGU_SIZE as well. pub fn size_estimate(&self, tcx: TyCtxt<'tcx>) -> usize { match *self { MonoItem::Fn(instance) => { @@ -221,7 +223,7 @@ impl<'tcx> MonoItem<'tcx> { impl<'tcx> fmt::Display for MonoItem<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - MonoItem::Fn(instance) => write!(f, "fn {}", instance), + MonoItem::Fn(instance) => write!(f, "fn {instance}"), MonoItem::Static(def_id) => { write!(f, "static {}", Instance::new(def_id, GenericArgs::empty())) } @@ -248,8 +250,14 @@ pub struct CodegenUnit<'tcx> { /// Auxiliary info about a `MonoItem`. #[derive(Copy, Clone, PartialEq, Debug, HashStable)] pub struct MonoItemData { + /// A cached copy of the result of `MonoItem::instantiation_mode`, where + /// `GloballyShared` maps to `false` and `LocalCopy` maps to `true`. + pub inlined: bool, + pub linkage: Linkage, pub visibility: Visibility, + + /// A cached copy of the result of `MonoItem::size_estimate`. pub size_estimate: usize, } @@ -526,17 +534,17 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> { format!("{}.{:08x}{}", tcx.crate_name(cnum), stable_crate_id, local_crate_id) }); - write!(cgu_name, "{}", crate_prefix).unwrap(); + write!(cgu_name, "{crate_prefix}").unwrap(); // Add the components for component in components { - write!(cgu_name, "-{}", component).unwrap(); + write!(cgu_name, "-{component}").unwrap(); } if let Some(special_suffix) = special_suffix { // We add a dot in here so it cannot clash with anything in a regular // Rust identifier - write!(cgu_name, ".{}", special_suffix).unwrap(); + write!(cgu_name, ".{special_suffix}").unwrap(); } Symbol::intern(&cgu_name) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8cbab31451b..27e39137092 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -124,14 +124,14 @@ fn dump_matched_mir_node<'tcx, F>( let def_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); // ignore-tidy-odd-backticks the literal below is fine - write!(file, "// MIR for `{}", def_path)?; + write!(file, "// MIR for `{def_path}")?; match body.source.promoted { None => write!(file, "`")?, - Some(promoted) => write!(file, "::{:?}`", promoted)?, + Some(promoted) => write!(file, "::{promoted:?}`")?, } - writeln!(file, " {} {}", disambiguator, pass_name)?; + writeln!(file, " {disambiguator} {pass_name}")?; if let Some(ref layout) = body.generator_layout() { - writeln!(file, "/* generator_layout = {:#?} */", layout)?; + writeln!(file, "/* generator_layout = {layout:#?} */")?; } writeln!(file)?; extra_data(PassWhere::BeforeCFG, &mut file)?; @@ -169,7 +169,7 @@ fn dump_file_basename<'tcx>( ) -> String { let source = body.source; let promotion_id = match source.promoted { - Some(id) => format!("-{:?}", id), + Some(id) => format!("-{id:?}"), None => String::new(), }; @@ -203,8 +203,7 @@ fn dump_file_basename<'tcx>( }; format!( - "{}.{}{}{}{}.{}.{}", - crate_name, item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator, + "{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}", ) } @@ -215,7 +214,7 @@ fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf { let mut file_path = PathBuf::new(); file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir)); - let file_name = format!("{}.{}", basename, extension,); + let file_name = format!("{basename}.{extension}",); file_path.push(&file_name); @@ -233,12 +232,12 @@ fn create_dump_file_with_basename( fs::create_dir_all(parent).map_err(|e| { io::Error::new( e.kind(), - format!("IO error creating MIR dump directory: {:?}; {}", parent, e), + format!("IO error creating MIR dump directory: {parent:?}; {e}"), ) })?; } Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| { - io::Error::new(e.kind(), format!("IO error creating MIR dump file: {:?}; {}", file_path, e)) + io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) })?)) } @@ -346,28 +345,24 @@ where // Basic block label at the top. let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" }; - writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?; + writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?; // List of statements in the middle. let mut current_location = Location { block, statement_index: 0 }; for statement in &data.statements { extra_data(PassWhere::BeforeLocation(current_location), w)?; - let indented_body = format!("{0}{0}{1:?};", INDENT, statement); + let indented_body = format!("{INDENT}{INDENT}{statement:?};"); if tcx.sess.opts.unstable_opts.mir_include_spans { writeln!( w, "{:A$} // {}{}", indented_body, - if tcx.sess.verbose() { - format!("{:?}: ", current_location) - } else { - String::new() - }, + if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() }, comment(tcx, statement.source_info), A = ALIGN, )?; } else { - writeln!(w, "{}", indented_body)?; + writeln!(w, "{indented_body}")?; } write_extra(tcx, w, |visitor| { @@ -387,12 +382,12 @@ where w, "{:A$} // {}{}", indented_terminator, - if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() }, + if tcx.sess.verbose() { format!("{current_location:?}: ") } else { String::new() }, comment(tcx, data.terminator().source_info), A = ALIGN, )?; } else { - writeln!(w, "{}", indented_terminator)?; + writeln!(w, "{indented_terminator}")?; } write_extra(tcx, w, |visitor| { @@ -402,7 +397,7 @@ where extra_data(PassWhere::AfterLocation(current_location), w)?; extra_data(PassWhere::AfterTerminator(block), w)?; - writeln!(w, "{}}}", INDENT) + writeln!(w, "{INDENT}}}") } /// After we print the main statement, we sometimes dump extra @@ -457,25 +452,25 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { self.tcx.sess.source_map().span_to_embeddable_string(*span) )); if let Some(user_ty) = user_ty { - self.push(&format!("+ user_ty: {:?}", user_ty)); + self.push(&format!("+ user_ty: {user_ty:?}")); } // FIXME: this is a poor version of `pretty_print_const_value`. let fmt_val = |val: &ConstValue<'tcx>| match val { ConstValue::ZeroSized => "<ZST>".to_string(), - ConstValue::Scalar(s) => format!("Scalar({:?})", s), + ConstValue::Scalar(s) => format!("Scalar({s:?})"), ConstValue::Slice { .. } => "Slice(..)".to_string(), ConstValue::ByRef { .. } => "ByRef(..)".to_string(), }; let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { - ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({:?})", leaf), + ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({leaf:?})"), ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(), }; let val = match literal { ConstantKind::Ty(ct) => match ct.kind() { - ty::ConstKind::Param(p) => format!("Param({})", p), + ty::ConstKind::Param(p) => format!("Param({p})"), ty::ConstKind::Unevaluated(uv) => { format!("Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) } @@ -514,20 +509,20 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> { match **kind { AggregateKind::Closure(def_id, args) => { self.push("closure"); - self.push(&format!("+ def_id: {:?}", def_id)); - self.push(&format!("+ args: {:#?}", args)); + self.push(&format!("+ def_id: {def_id:?}")); + self.push(&format!("+ args: {args:#?}")); } AggregateKind::Generator(def_id, args, movability) => { self.push("generator"); - self.push(&format!("+ def_id: {:?}", def_id)); - self.push(&format!("+ args: {:#?}", args)); - self.push(&format!("+ movability: {:?}", movability)); + self.push(&format!("+ def_id: {def_id:?}")); + self.push(&format!("+ args: {args:#?}")); + self.push(&format!("+ movability: {movability:?}")); } AggregateKind::Adt(_, _, _, Some(user_ty), _) => { self.push("adt"); - self.push(&format!("+ user_ty: {:?}", user_ty)); + self.push(&format!("+ user_ty: {user_ty:?}")); } _ => {} @@ -578,7 +573,7 @@ fn write_scope_tree( comment(tcx, var_debug_info.source_info), )?; } else { - writeln!(w, "{}", indented_debug_info)?; + writeln!(w, "{indented_debug_info}")?; } } @@ -600,7 +595,7 @@ fn write_scope_tree( format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty); if let Some(user_ty) = &local_decl.user_ty { for user_ty in user_ty.projections() { - write!(indented_decl, " as {:?}", user_ty).unwrap(); + write!(indented_decl, " as {user_ty:?}").unwrap(); } } indented_decl.push(';'); @@ -617,7 +612,7 @@ fn write_scope_tree( comment(tcx, local_decl.source_info), )?; } else { - writeln!(w, "{}", indented_decl,)?; + writeln!(w, "{indented_decl}",)?; } } @@ -654,10 +649,10 @@ fn write_scope_tree( tcx.sess.source_map().span_to_embeddable_string(span), )?; } else { - writeln!(w, "{}", indented_header)?; + writeln!(w, "{indented_header}")?; } } else { - writeln!(w, "{}", indented_header)?; + writeln!(w, "{indented_header}")?; } write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; @@ -844,7 +839,7 @@ fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fm for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) { write!(w, " ")?; } - writeln!(w, " │ {}", ascii) + writeln!(w, " │ {ascii}") } /// Number of bytes to print per allocation hex dump line. @@ -880,7 +875,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( if num_lines > 0 { write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?; } else { - write!(w, "{}", prefix)?; + write!(w, "{prefix}")?; } let mut i = Size::ZERO; @@ -913,10 +908,10 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( let offset = Size::from_bytes(offset); let provenance_width = |bytes| bytes * 3; let ptr = Pointer::new(prov, offset); - let mut target = format!("{:?}", ptr); + let mut target = format!("{ptr:?}"); if target.len() > provenance_width(ptr_size.bytes_usize() - 1) { // This is too long, try to save some space. - target = format!("{:#?}", ptr); + target = format!("{ptr:#?}"); } if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE { // This branch handles the situation where a provenance starts in the current line @@ -935,10 +930,10 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; ascii.clear(); - write!(w, "{0:─^1$}╼", target, overflow_width)?; + write!(w, "{target:─^overflow_width$}╼")?; } else { oversized_ptr(&mut target, remainder_width); - write!(w, "╾{0:─^1$}", target, remainder_width)?; + write!(w, "╾{target:─^remainder_width$}")?; line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?; write!(w, "{0:─^1$}╼", "", overflow_width)?; @@ -955,7 +950,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( let provenance_width = provenance_width(ptr_size.bytes_usize() - 1); oversized_ptr(&mut target, provenance_width); ascii.push('╾'); - write!(w, "╾{0:─^1$}╼", target, provenance_width)?; + write!(w, "╾{target:─^provenance_width$}╼")?; for _ in 0..ptr_size.bytes() - 2 { ascii.push('─'); } @@ -972,7 +967,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Format is similar to "oversized" above. let j = i.bytes_usize(); let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "╾{:02x}{:#?} (1 ptr byte)╼", c, prov)?; + write!(w, "╾{c:02x}{prov:#?} (1 ptr byte)╼")?; i += Size::from_bytes(1); } else if alloc .init_mask() @@ -984,7 +979,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Checked definedness (and thus range) and provenance. This access also doesn't // influence interpreter execution but is only for debugging. let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "{:02x}", c)?; + write!(w, "{c:02x}")?; if c.is_ascii_control() || c >= 0x80 { ascii.push('.'); } else { @@ -1018,7 +1013,7 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Res _ => tcx.is_closure(def_id), }; match (kind, body.source.promoted) { - (_, Some(i)) => write!(w, "{:?} in ", i)?, + (_, Some(i)) => write!(w, "{i:?} in ")?, (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, (DefKind::Static(hir::Mutability::Not), _) => write!(w, "static ")?, (DefKind::Static(hir::Mutability::Mut), _) => write!(w, "static mut ")?, @@ -1051,7 +1046,7 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Res if let Some(yield_ty) = body.yield_ty() { writeln!(w)?; - writeln!(w, "yields {}", yield_ty)?; + writeln!(w, "yields {yield_ty}")?; } write!(w, " ")?; diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index e8cb9860ee5..71bec49af93 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -198,7 +198,7 @@ impl Debug for GeneratorLayout<'_> { if fmt.alternate() { write!(fmt, "{:9}({:?})", variant_name, self.0) } else { - write!(fmt, "{}", variant_name) + write!(fmt, "{variant_name}") } } } diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs index 730c551576a..20a9e6889e4 100644 --- a/compiler/rustc_middle/src/mir/spanview.rs +++ b/compiler/rustc_middle/src/mir/spanview.rs @@ -159,10 +159,10 @@ where indent_to_initial_start_col, source_map.span_to_snippet(spanview_span).expect("function should have printable source") ); - writeln!(w, "{}", HEADER)?; - writeln!(w, "<title>{}</title>", title)?; - writeln!(w, "{}", STYLE_SECTION)?; - writeln!(w, "{}", START_BODY)?; + writeln!(w, "{HEADER}")?; + writeln!(w, "<title>{title}</title>")?; + writeln!(w, "{STYLE_SECTION}")?; + writeln!(w, "{START_BODY}")?; write!( w, r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#, @@ -226,7 +226,7 @@ where write_coverage_gap(tcx, from_pos, end_pos, w)?; } writeln!(w, r#"</span></div>"#)?; - writeln!(w, "{}", FOOTER)?; + writeln!(w, "{FOOTER}")?; Ok(()) } @@ -561,17 +561,16 @@ where } for (i, line) in html_snippet.lines().enumerate() { if i > 0 { - write!(w, "{}", NEW_LINE_SPAN)?; + write!(w, "{NEW_LINE_SPAN}")?; } write!( w, - r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#, - maybe_alt_class, layer, maybe_title_attr, line + r#"<span class="code{maybe_alt_class}" style="--layer: {layer}"{maybe_title_attr}>{line}</span>"# )?; } // Check for and translate trailing newlines, because `str::lines()` ignores them if html_snippet.ends_with('\n') { - write!(w, "{}", NEW_LINE_SPAN)?; + write!(w, "{NEW_LINE_SPAN}")?; } if layer == 1 { write!(w, "</span>")?; diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 1b9c1438f40..6de84351595 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -280,7 +280,7 @@ impl<'tcx> Debug for TerminatorKind<'tcx> { match (successor_count, unwind) { (0, None) => Ok(()), - (0, Some(unwind)) => write!(fmt, " -> {}", unwind), + (0, Some(unwind)) => write!(fmt, " -> {unwind}"), (1, None) => write!(fmt, " -> {:?}", self.successors().next().unwrap()), _ => { write!(fmt, " -> [")?; @@ -307,22 +307,22 @@ impl<'tcx> TerminatorKind<'tcx> { use self::TerminatorKind::*; match self { Goto { .. } => write!(fmt, "goto"), - SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr), + SwitchInt { discr, .. } => write!(fmt, "switchInt({discr:?})"), Return => write!(fmt, "return"), GeneratorDrop => write!(fmt, "generator_drop"), Resume => write!(fmt, "resume"), Terminate => write!(fmt, "abort"), - Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value), + Yield { value, resume_arg, .. } => write!(fmt, "{resume_arg:?} = yield({value:?})"), Unreachable => write!(fmt, "unreachable"), - Drop { place, .. } => write!(fmt, "drop({:?})", place), + Drop { place, .. } => write!(fmt, "drop({place:?})"), Call { func, args, destination, .. } => { - write!(fmt, "{:?} = ", destination)?; - write!(fmt, "{:?}(", func)?; + write!(fmt, "{destination:?} = ")?; + write!(fmt, "{func:?}(")?; for (index, arg) in args.iter().enumerate() { if index > 0 { write!(fmt, ", ")?; } - write!(fmt, "{:?}", arg)?; + write!(fmt, "{arg:?}")?; } write!(fmt, ")") } @@ -331,7 +331,7 @@ impl<'tcx> TerminatorKind<'tcx> { if !expected { write!(fmt, "!")?; } - write!(fmt, "{:?}, ", cond)?; + write!(fmt, "{cond:?}, ")?; msg.fmt_assert_args(fmt)?; write!(fmt, ")") } @@ -344,7 +344,7 @@ impl<'tcx> TerminatorKind<'tcx> { let print_late = |&late| if late { "late" } else { "" }; match op { InlineAsmOperand::In { reg, value } => { - write!(fmt, "in({}) {:?}", reg, value)?; + write!(fmt, "in({reg}) {value:?}")?; } InlineAsmOperand::Out { reg, late, place: Some(place) } => { write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?; @@ -371,17 +371,17 @@ impl<'tcx> TerminatorKind<'tcx> { write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?; } InlineAsmOperand::Const { value } => { - write!(fmt, "const {:?}", value)?; + write!(fmt, "const {value:?}")?; } InlineAsmOperand::SymFn { value } => { - write!(fmt, "sym_fn {:?}", value)?; + write!(fmt, "sym_fn {value:?}")?; } InlineAsmOperand::SymStatic { def_id } => { - write!(fmt, "sym_static {:?}", def_id)?; + write!(fmt, "sym_static {def_id:?}")?; } } } - write!(fmt, ", options({:?}))", options) + write!(fmt, ", options({options:?}))") } } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index c304245ca39..63df2830e0a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -885,6 +885,13 @@ rustc_queries! { desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } } + /// We need to store the assumed_wf_types for an RPITIT so that impls of foreign + /// traits with return-position impl trait in traits can inherit the right wf types. + query assumed_wf_types_for_rpitit(key: DefId) -> &'tcx [(Ty<'tcx>, Span)] { + desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) } + separate_provide_extern + } + /// Computes the signature of the function. query fn_sig(key: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> { desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) } @@ -898,6 +905,10 @@ rustc_queries! { desc { |tcx| "linting {}", describe_as_module(key, tcx) } } + query check_unused_traits(_: ()) -> () { + desc { "checking unused trait imports in crate" } + } + /// Checks the attributes in the module. query check_mod_attrs(key: LocalDefId) -> () { desc { |tcx| "checking attributes in {}", describe_as_module(key, tcx) } @@ -1273,7 +1284,7 @@ rustc_queries! { query vtable_allocation(key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>)) -> mir::interpret::AllocId { desc { |tcx| "vtable const allocation for <{} as {}>", key.0, - key.1.map(|trait_ref| format!("{}", trait_ref)).unwrap_or("_".to_owned()) + key.1.map(|trait_ref| format!("{trait_ref}")).unwrap_or("_".to_owned()) } } @@ -1579,7 +1590,7 @@ rustc_queries! { } /// Returns a list of all `extern` blocks of a crate. - query foreign_modules(_: CrateNum) -> &'tcx FxHashMap<DefId, ForeignModule> { + query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap<DefId, ForeignModule> { arena_cache desc { "looking up the foreign modules of a linked crate" } separate_provide_extern @@ -2060,9 +2071,9 @@ rustc_queries! { } } - query is_impossible_method(key: (DefId, DefId)) -> bool { + query is_impossible_associated_item(key: (DefId, DefId)) -> bool { desc { |tcx| - "checking if `{}` is impossible to call within `{}`", + "checking if `{}` is impossible to reference within `{}`", tcx.def_path_str(key.1), tcx.def_path_str(key.0), } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e070b054720..8e2e71fd879 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -659,7 +659,7 @@ impl<'tcx> Pat<'tcx> { impl<'tcx> IntoDiagnosticArg for Pat<'tcx> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - format!("{}", self).into_diagnostic_arg() + format!("{self}").into_diagnostic_arg() } } @@ -789,7 +789,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { match self.kind { PatKind::Wild => write!(f, "_"), - PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{}: _", subpattern), + PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"), PatKind::Binding { mutability, name, mode, ref subpattern, .. } => { let is_mut = match mode { BindingMode::ByValue => mutability == Mutability::Mut, @@ -801,9 +801,9 @@ impl<'tcx> fmt::Display for Pat<'tcx> { if is_mut { write!(f, "mut ")?; } - write!(f, "{}", name)?; + write!(f, "{name}")?; if let Some(ref subpattern) = *subpattern { - write!(f, " @ {}", subpattern)?; + write!(f, " @ {subpattern}")?; } Ok(()) } @@ -833,7 +833,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { }; if let Some((variant, name)) = &variant_and_name { - write!(f, "{}", name)?; + write!(f, "{name}")?; // Only for Adt we can have `S {...}`, // which we handle separately here. @@ -893,13 +893,13 @@ impl<'tcx> fmt::Display for Pat<'tcx> { } _ => bug!("{} is a bad Deref pattern type", self.ty), } - write!(f, "{}", subpattern) + write!(f, "{subpattern}") } - PatKind::Constant { value } => write!(f, "{}", value), + PatKind::Constant { value } => write!(f, "{value}"), PatKind::Range(box PatRange { lo, hi, end }) => { - write!(f, "{}", lo)?; - write!(f, "{}", end)?; - write!(f, "{}", hi) + write!(f, "{lo}")?; + write!(f, "{end}")?; + write!(f, "{hi}") } PatKind::Slice { ref prefix, ref slice, ref suffix } | PatKind::Array { ref prefix, ref slice, ref suffix } => { @@ -911,7 +911,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { write!(f, "{}", start_or_comma())?; match slice.kind { PatKind::Wild => {} - _ => write!(f, "{}", slice)?, + _ => write!(f, "{slice}")?, } write!(f, "..")?; } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b7ffed57a0b..85116555fc0 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -649,43 +649,31 @@ pub enum ImplSource<'tcx, N> { /// for some type parameter. The `Vec<N>` represents the /// obligations incurred from normalizing the where-clause (if /// any). - Param(Vec<N>, ty::BoundConstness), + Param(ty::BoundConstness, Vec<N>), - /// Virtual calls through an object. - Object(ImplSourceObjectData<N>), - - /// Successful resolution for a builtin trait. - Builtin(Vec<N>), - - /// ImplSource for trait upcasting coercion - TraitUpcasting(ImplSourceTraitUpcastingData<N>), + /// Successful resolution for a builtin impl. + Builtin(BuiltinImplSource, Vec<N>), } impl<'tcx, N> ImplSource<'tcx, N> { pub fn nested_obligations(self) -> Vec<N> { match self { ImplSource::UserDefined(i) => i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, - ImplSource::Object(d) => d.nested, - ImplSource::TraitUpcasting(d) => d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, } } pub fn borrow_nested_obligations(&self) -> &[N] { match self { ImplSource::UserDefined(i) => &i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n, - ImplSource::Object(d) => &d.nested, - ImplSource::TraitUpcasting(d) => &d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => &n, } } pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { match self { ImplSource::UserDefined(i) => &mut i.nested, - ImplSource::Param(n, _) | ImplSource::Builtin(n) => n, - ImplSource::Object(d) => &mut d.nested, - ImplSource::TraitUpcasting(d) => &mut d.nested, + ImplSource::Param(_, n) | ImplSource::Builtin(_, n) => n, } } @@ -699,17 +687,9 @@ impl<'tcx, N> ImplSource<'tcx, N> { args: i.args, nested: i.nested.into_iter().map(f).collect(), }), - ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct), - ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()), - ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData { - vtable_base: o.vtable_base, - nested: o.nested.into_iter().map(f).collect(), - }), - ImplSource::TraitUpcasting(d) => { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { - vtable_vptr_slot: d.vtable_vptr_slot, - nested: d.nested.into_iter().map(f).collect(), - }) + ImplSource::Param(ct, n) => ImplSource::Param(ct, n.into_iter().map(f).collect()), + ImplSource::Builtin(source, n) => { + ImplSource::Builtin(source, n.into_iter().map(f).collect()) } } } @@ -733,29 +713,31 @@ pub struct ImplSourceUserDefinedData<'tcx, N> { pub nested: Vec<N>, } -#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct ImplSourceTraitUpcastingData<N> { +#[derive(Copy, Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Debug)] +pub enum BuiltinImplSource { + /// Some builtin impl we don't need to differentiate. This should be used + /// unless more specific information is necessary. + Misc, + /// A builtin impl for trait objects. + /// + /// The vtable is formed by concatenating together the method lists of + /// the base object trait and all supertraits, pointers to supertrait vtable will + /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods + /// in that vtable. + Object { vtable_base: usize }, /// The vtable is formed by concatenating together the method lists of /// the base object trait and all supertraits, pointers to supertrait vtable will /// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable /// within that vtable. - pub vtable_vptr_slot: Option<usize>, - - pub nested: Vec<N>, + TraitUpcasting { vtable_vptr_slot: Option<usize> }, + /// Unsizing a tuple like `(A, B, ..., X)` to `(A, B, ..., Y)` if `X` unsizes to `Y`. + /// + /// This needs to be a separate variant as it is still unstable and we need to emit + /// a feature error when using it on stable. + TupleUnsizing, } -#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)] -#[derive(TypeFoldable, TypeVisitable)] -pub struct ImplSourceObjectData<N> { - /// The vtable is formed by concatenating together the method lists of - /// the base object trait and all supertraits, pointers to supertrait vtable will - /// be provided when necessary; this is the start of `upcast_trait_ref`'s methods - /// in that vtable. - pub vtable_base: usize, - - pub nested: Vec<N>, -} +TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource } #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)] pub enum ObjectSafetyViolation { @@ -795,49 +777,48 @@ impl ObjectSafetyViolation { "where clause cannot reference non-lifetime `for<...>` variables".into() } ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { - format!("associated function `{}` has no `self` parameter", name).into() + format!("associated function `{name}` has no `self` parameter").into() } ObjectSafetyViolation::Method( name, MethodViolationCode::ReferencesSelfInput(_), DUMMY_SP, - ) => format!("method `{}` references the `Self` type in its parameters", name).into(), + ) => format!("method `{name}` references the `Self` type in its parameters").into(), ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfInput(_), _) => { - format!("method `{}` references the `Self` type in this parameter", name).into() + format!("method `{name}` references the `Self` type in this parameter").into() } ObjectSafetyViolation::Method(name, MethodViolationCode::ReferencesSelfOutput, _) => { - format!("method `{}` references the `Self` type in its return type", name).into() + format!("method `{name}` references the `Self` type in its return type").into() } ObjectSafetyViolation::Method( name, MethodViolationCode::ReferencesImplTraitInTrait(_), _, - ) => format!("method `{}` references an `impl Trait` type in its return type", name) - .into(), + ) => { + format!("method `{name}` references an `impl Trait` type in its return type").into() + } ObjectSafetyViolation::Method(name, MethodViolationCode::AsyncFn, _) => { - format!("method `{}` is `async`", name).into() + format!("method `{name}` is `async`").into() } ObjectSafetyViolation::Method( name, MethodViolationCode::WhereClauseReferencesSelf, _, - ) => { - format!("method `{}` references the `Self` type in its `where` clause", name).into() - } + ) => format!("method `{name}` references the `Self` type in its `where` clause").into(), ObjectSafetyViolation::Method(name, MethodViolationCode::Generic, _) => { - format!("method `{}` has generic type parameters", name).into() + format!("method `{name}` has generic type parameters").into() } ObjectSafetyViolation::Method( name, MethodViolationCode::UndispatchableReceiver(_), _, - ) => format!("method `{}`'s `self` parameter cannot be dispatched on", name).into(), + ) => format!("method `{name}`'s `self` parameter cannot be dispatched on").into(), ObjectSafetyViolation::AssocConst(name, DUMMY_SP) => { - format!("it contains associated `const` `{}`", name).into() + format!("it contains associated `const` `{name}`").into() } ObjectSafetyViolation::AssocConst(..) => "it contains this associated `const`".into(), ObjectSafetyViolation::GAT(name, _) => { - format!("it contains the generic associated type `{}`", name).into() + format!("it contains the generic associated type `{name}`").into() } } } @@ -855,8 +836,7 @@ impl ObjectSafetyViolation { err.span_suggestion( add_self_sugg.1, format!( - "consider turning `{}` into a method by giving it a `&self` argument", - name + "consider turning `{name}` into a method by giving it a `&self` argument" ), add_self_sugg.0.to_string(), Applicability::MaybeIncorrect, @@ -864,9 +844,8 @@ impl ObjectSafetyViolation { err.span_suggestion( make_sized_sugg.1, format!( - "alternatively, consider constraining `{}` so it does not apply to \ - trait objects", - name + "alternatively, consider constraining `{name}` so it does not apply to \ + trait objects" ), make_sized_sugg.0.to_string(), Applicability::MaybeIncorrect, @@ -879,7 +858,7 @@ impl ObjectSafetyViolation { ) => { err.span_suggestion( *span, - format!("consider changing method `{}`'s `self` parameter to be `&self`", name), + format!("consider changing method `{name}`'s `self` parameter to be `&self`"), "&Self", Applicability::MachineApplicable, ); @@ -887,7 +866,7 @@ impl ObjectSafetyViolation { ObjectSafetyViolation::AssocConst(name, _) | ObjectSafetyViolation::GAT(name, _) | ObjectSafetyViolation::Method(name, ..) => { - err.help(format!("consider moving `{}` to another trait", name)); + err.help(format!("consider moving `{name}` to another trait")); } } } diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index f2dda003b99..a90d58f5fc1 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -304,9 +304,7 @@ impl From<ErrorGuaranteed> for OverflowError { } } -TrivialTypeTraversalAndLiftImpls! { - OverflowError, -} +TrivialTypeTraversalAndLiftImpls! { OverflowError } impl<'tcx> From<OverflowError> for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 73b332fd8ec..b21a00e4122 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -57,6 +57,7 @@ pub enum Certainty { impl Certainty { pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + pub const OVERFLOW: Certainty = Certainty::Maybe(MaybeCause::Overflow); /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -66,7 +67,7 @@ impl Certainty { /// success, we merge these two responses. This results in ambiguity. /// /// If we unify ambiguity with overflow, we return overflow. This doesn't matter - /// inside of the solver as we distinguish ambiguity from overflow. It does + /// inside of the solver as we do not distinguish ambiguity from overflow. It does /// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar` /// in ambiguity without changing the inference state, we still want to tell the /// user that `T: Baz` results in overflow. diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index 8698cf86022..e793f481995 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -73,8 +73,12 @@ pub struct GoalCandidate<'tcx> { pub enum CandidateKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, + DynUpcastingAssembly, /// A normal candidate for proving a goal - Candidate { name: String, result: QueryResult<'tcx> }, + Candidate { + name: String, + result: QueryResult<'tcx>, + }, } impl Debug for GoalCandidate<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index f19f1189e44..f1e567e9bf8 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -15,11 +15,11 @@ struct Indentor<'a, 'b> { impl Write for Indentor<'_, '_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { - for line in s.split_inclusive("\n") { + for line in s.split_inclusive('\n') { if self.on_newline { self.f.write_str(" ")?; } - self.on_newline = line.ends_with("\n"); + self.on_newline = line.ends_with('\n'); self.f.write_str(line)?; } @@ -68,7 +68,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { writeln!(self.f, "NESTED GOALS ADDED TO CALLER: [")?; self.nested(|this| { for goal in goal.returned_goals.iter() { - writeln!(this.f, "ADDED GOAL: {:?},", goal)?; + writeln!(this.f, "ADDED GOAL: {goal:?},")?; } Ok(()) })?; @@ -100,8 +100,11 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { CandidateKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } + CandidateKind::DynUpcastingAssembly => { + writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:") + } CandidateKind::Candidate { name, result } => { - writeln!(self.f, "CANDIDATE {}: {:?}", name, result) + writeln!(self.f, "CANDIDATE {name}: {result:?}") } }?; diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 18a57b6181a..e48b46d12c4 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -43,7 +43,7 @@ impl Graph { /// The parent of a given impl, which is the `DefId` of the trait when the /// impl is a "specialization root". pub fn parent(&self, child: DefId) -> DefId { - *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {:?}", child)) + *self.parent.get(&child).unwrap_or_else(|| panic!("Failed to get parent for {child:?}")) } } diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs index e2cd118500b..d7dc429f53b 100644 --- a/compiler/rustc_middle/src/traits/structural_impls.rs +++ b/compiler/rustc_middle/src/traits/structural_impls.rs @@ -6,18 +6,16 @@ use std::fmt; impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v), + match self { + super::ImplSource::UserDefined(v) => write!(f, "{v:?}"), - super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d), - - super::ImplSource::Object(ref d) => write!(f, "{:?}", d), - - super::ImplSource::Param(ref n, ct) => { - write!(f, "ImplSourceParamData({:?}, {:?})", n, ct) + super::ImplSource::Builtin(source, d) => { + write!(f, "Builtin({source:?}, {d:?})") } - super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d), + super::ImplSource::Param(ct, n) => { + write!(f, "ImplSourceParamData({n:?}, {ct:?})") + } } } } @@ -31,23 +29,3 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx, ) } } - -impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})", - self.vtable_vptr_slot, self.nested - ) - } -} - -impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<N> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "ImplSourceObjectData(vtable_base={}, nested={:?})", - self.vtable_base, self.nested - ) - } -} diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 0364a620810..cdd8351499b 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -27,9 +27,7 @@ impl From<ErrorGuaranteed> for NotConstEvaluatable { } } -TrivialTypeTraversalAndLiftImpls! { - NotConstEvaluatable, -} +TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable } pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>; diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs index a5b05a4f9b5..2fec8ac9095 100644 --- a/compiler/rustc_middle/src/ty/binding.rs +++ b/compiler/rustc_middle/src/ty/binding.rs @@ -6,7 +6,7 @@ pub enum BindingMode { BindByValue(Mutability), } -TrivialTypeTraversalAndLiftImpls! { BindingMode, } +TrivialTypeTraversalAndLiftImpls! { BindingMode } impl BindingMode { pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode { diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 91eefa2c125..42f22604975 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -348,7 +348,7 @@ pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tc for (i, proj) in place.projections.iter().enumerate() { match proj.kind { HirProjectionKind::Deref => { - curr_string = format!("*{}", curr_string); + curr_string = format!("*{curr_string}"); } HirProjectionKind::Field(idx, variant) => match place.ty_before_projection(i).kind() { ty::Adt(def, ..) => { diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index b4f4f9bef8e..7c05deae90a 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -168,7 +168,6 @@ impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::ParamEnv<'tcx> { fn encode(&self, e: &mut E) { self.caller_bounds().encode(e); self.reveal().encode(e); - self.constness().encode(e); } } @@ -306,8 +305,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::ParamEnv<'tcx> { fn decode(d: &mut D) -> Self { let caller_bounds = Decodable::decode(d); let reveal = Decodable::decode(d); - let constness = Decodable::decode(d); - ty::ParamEnv::new(caller_bounds, reveal, constness) + ty::ParamEnv::new(caller_bounds, reveal) } } diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index bf9f5846ed9..cce10417e1b 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -212,7 +212,7 @@ impl<'tcx> Const<'tcx> { Err(e) => { tcx.sess.delay_span_bug( expr.span, - format!("Const::from_anon_const: couldn't lit_to_const {:?}", e), + format!("Const::from_anon_const: couldn't lit_to_const {e:?}"), ); } } @@ -267,7 +267,7 @@ impl<'tcx> Const<'tcx> { pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> Self { let size = tcx .layout_of(ty) - .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) + .unwrap_or_else(|e| panic!("could not compute layout for {ty:?}: {e:?}")) .size; ty::Const::new_value( tcx, @@ -294,6 +294,14 @@ impl<'tcx> Const<'tcx> { Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize)) } + /// Attempts to convert to a `ValTree` + pub fn try_to_valtree(self) -> Option<ty::ValTree<'tcx>> { + match self.kind() { + ty::ConstKind::Value(valtree) => Some(valtree), + _ => None, + } + } + #[inline] /// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of /// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 624195cfb28..b16163edf14 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -463,7 +463,7 @@ impl TryFrom<ScalarInt> for Double { impl fmt::Debug for ScalarInt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Dispatch to LowerHex below. - write!(f, "0x{:x}", self) + write!(f, "0x{self:x}") } } diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 6c76075c214..db4a15fbee5 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -17,7 +17,7 @@ pub struct UnevaluatedConst<'tcx> { impl rustc_errors::IntoDiagnosticArg for UnevaluatedConst<'_> { fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { - format!("{:?}", self).into_diagnostic_arg() + format!("{self:?}").into_diagnostic_arg() } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8bb13edbe95..aa1f8e48b1b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -569,6 +569,7 @@ pub struct GlobalCtxt<'tcx> { /// Caches the results of goal evaluation in the new solver. pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>, + pub new_solver_coherence_evaluation_cache: solve::EvaluationCache<'tcx>, /// Data layout specification for the current target. pub data_layout: TargetDataLayout, @@ -680,10 +681,12 @@ impl<'tcx> TyCtxt<'tcx> { value.lift_to_tcx(self) } - /// Creates a type context and call the closure with a `TyCtxt` reference - /// to the context. The closure enforces that the type context and any interned - /// value (types, args, etc.) can only be used while `ty::tls` has a valid - /// reference to the context, to allow formatting values that need it. + /// Creates a type context. To use the context call `fn enter` which + /// provides a `TyCtxt`. + /// + /// By only providing the `TyCtxt` inside of the closure we enforce that the type + /// context and any interned alue (types, args, etc.) can only be used while `ty::tls` + /// has a valid reference to the context, to allow formatting values that need it. pub fn create_global_ctxt( s: &'tcx Session, lint_store: Lrc<dyn Any + sync::DynSend + sync::DynSync>, @@ -721,6 +724,7 @@ impl<'tcx> TyCtxt<'tcx> { selection_cache: Default::default(), evaluation_cache: Default::default(), new_solver_evaluation_cache: Default::default(), + new_solver_coherence_evaluation_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), } @@ -1860,6 +1864,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`, /// typically generated by `#[derive(LintDiagnostic)]`). + #[track_caller] pub fn emit_spanned_lint( self, lint: &'static Lint, @@ -1880,6 +1885,7 @@ impl<'tcx> TyCtxt<'tcx> { /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_span_lint_hir( self, lint: &'static Lint, @@ -1896,6 +1902,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Emit a lint from a lint struct (some type that implements `DecorateLint`, typically /// generated by `#[derive(LintDiagnostic)]`). + #[track_caller] pub fn emit_lint( self, lint: &'static Lint, @@ -1911,6 +1918,7 @@ impl<'tcx> TyCtxt<'tcx> { /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_lint_node( self, lint: &'static Lint, diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 905e855896e..8570f83dcc6 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -126,7 +126,7 @@ pub fn suggest_arbitrary_trait_bound<'tcx>( if constraint.ends_with('>') { constraint = format!("{}, {} = {}>", &constraint[..constraint.len() - 1], name, term); } else { - constraint.push_str(&format!("<{} = {}>", name, term)); + constraint.push_str(&format!("<{name} = {term}>")); } } @@ -274,9 +274,9 @@ pub fn suggest_constraining_type_params<'a>( if span_to_replace.is_some() { constraint.clone() } else if bound_list_non_empty { - format!(" + {}", constraint) + format!(" + {constraint}") } else { - format!(" {}", constraint) + format!(" {constraint}") }, SuggestChangingConstraintsMessage::RestrictBoundFurther, )) @@ -337,7 +337,7 @@ pub fn suggest_constraining_type_params<'a>( generics.tail_span_for_predicate_suggestion(), constraints .iter() - .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint)) + .map(|&(constraint, _)| format!(", {param_name}: {constraint}")) .collect::<String>(), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); @@ -358,7 +358,7 @@ pub fn suggest_constraining_type_params<'a>( // default (`<T=Foo>`), so we suggest adding `where T: Bar`. suggestions.push(( generics.tail_span_for_predicate_suggestion(), - format!(" where {}: {}", param_name, constraint), + format!(" where {param_name}: {constraint}"), SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, )); continue; @@ -371,7 +371,7 @@ pub fn suggest_constraining_type_params<'a>( if let Some(colon_span) = param.colon_span { suggestions.push(( colon_span.shrink_to_hi(), - format!(" {}", constraint), + format!(" {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); continue; @@ -383,7 +383,7 @@ pub fn suggest_constraining_type_params<'a>( // - help: consider restricting this type parameter with `T: Foo` suggestions.push(( param.span.shrink_to_hi(), - format!(": {}", constraint), + format!(": {constraint}"), SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, )); } @@ -401,10 +401,10 @@ pub fn suggest_constraining_type_params<'a>( Cow::from("consider further restricting this bound") } SuggestChangingConstraintsMessage::RestrictType { ty } => { - Cow::from(format!("consider restricting type parameter `{}`", ty)) + Cow::from(format!("consider restricting type parameter `{ty}`")) } SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { - Cow::from(format!("consider further restricting type parameter `{}`", ty)) + Cow::from(format!("consider further restricting type parameter `{ty}`")) } SuggestChangingConstraintsMessage::RemoveMaybeUnsized => { Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`") diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index c794c3faded..bf6f082c21c 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -90,9 +90,9 @@ impl<'tcx> TypeError<'tcx> { // A naive approach to making sure that we're not reporting silly errors such as: // (expected closure, found closure). if expected == found { - format!("expected {}, found a different {}", expected, found) + format!("expected {expected}, found a different {found}") } else { - format!("expected {}, found {}", expected, found) + format!("expected {expected}, found {found}") } } @@ -131,7 +131,7 @@ impl<'tcx> TypeError<'tcx> { ) .into(), ArgCount => "incorrect number of function parameters".into(), - FieldMisMatch(adt, field) => format!("field type mismatch: {}.{}", adt, field).into(), + FieldMisMatch(adt, field) => format!("field type mismatch: {adt}.{field}").into(), RegionsDoesNotOutlive(..) => "lifetime mismatch".into(), // Actually naming the region here is a bit confusing because context is lacking RegionsInsufficientlyPolymorphic(..) => { @@ -164,7 +164,7 @@ impl<'tcx> TypeError<'tcx> { ty::IntVarValue::IntType(ty) => ty.name_str(), ty::IntVarValue::UintType(ty) => ty.name_str(), }; - format!("expected `{}`, found `{}`", expected, found).into() + format!("expected `{expected}`, found `{found}`").into() } FloatMismatch(ref values) => format!( "expected `{}`, found `{}`", @@ -339,12 +339,17 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) { - let width = self.sess.diagnostic_width(); - let length_limit = width.saturating_sub(30); let regular = FmtPrinter::new(self, hir::def::Namespace::TypeNS) .pretty_print_type(ty) .expect("could not write to `String`") .into_buffer(); + + if !self.sess.opts.unstable_opts.write_long_types_to_disk { + return (regular, None); + } + + let width = self.sess.diagnostic_width(); + let length_limit = width.saturating_sub(30); if regular.len() <= width { return (regular, None); } diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs index e86ff4d26aa..668aa4521c1 100644 --- a/compiler/rustc_middle/src/ty/fast_reject.rs +++ b/compiler/rustc_middle/src/ty/fast_reject.rs @@ -6,35 +6,33 @@ use std::fmt::Debug; use std::hash::Hash; use std::iter; -use self::SimplifiedType::*; - /// See `simplify_type`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] pub enum SimplifiedType { - BoolSimplifiedType, - CharSimplifiedType, - IntSimplifiedType(ty::IntTy), - UintSimplifiedType(ty::UintTy), - FloatSimplifiedType(ty::FloatTy), - AdtSimplifiedType(DefId), - ForeignSimplifiedType(DefId), - StrSimplifiedType, - ArraySimplifiedType, - SliceSimplifiedType, - RefSimplifiedType(Mutability), - PtrSimplifiedType(Mutability), - NeverSimplifiedType, - TupleSimplifiedType(usize), + Bool, + Char, + Int(ty::IntTy), + Uint(ty::UintTy), + Float(ty::FloatTy), + Adt(DefId), + Foreign(DefId), + Str, + Array, + Slice, + Ref(Mutability), + Ptr(Mutability), + Never, + Tuple(usize), /// A trait object, all of whose components are markers /// (e.g., `dyn Send + Sync`). - MarkerTraitObjectSimplifiedType, - TraitSimplifiedType(DefId), - ClosureSimplifiedType(DefId), - GeneratorSimplifiedType(DefId), - GeneratorWitnessSimplifiedType(usize), - GeneratorWitnessMIRSimplifiedType(DefId), - FunctionSimplifiedType(usize), - PlaceholderSimplifiedType, + MarkerTraitObject, + Trait(DefId), + Closure(DefId), + Generator(DefId), + GeneratorWitness(usize), + GeneratorWitnessMIR(DefId), + Function(usize), + Placeholder, } /// Generic parameters are pretty much just bound variables, e.g. @@ -64,6 +62,9 @@ pub enum TreatParams { /// correct mode for *lookup*, as during candidate selection. /// /// N.B. during deep rejection, this acts identically to `ForLookup`. + /// + /// FIXME(-Ztrait-solver=next): Remove this variant and cleanup + /// the code. NextSolverLookup, } @@ -110,34 +111,36 @@ pub fn simplify_type<'tcx>( treat_params: TreatParams, ) -> Option<SimplifiedType> { match *ty.kind() { - ty::Bool => Some(BoolSimplifiedType), - ty::Char => Some(CharSimplifiedType), - ty::Int(int_type) => Some(IntSimplifiedType(int_type)), - ty::Uint(uint_type) => Some(UintSimplifiedType(uint_type)), - ty::Float(float_type) => Some(FloatSimplifiedType(float_type)), - ty::Adt(def, _) => Some(AdtSimplifiedType(def.did())), - ty::Str => Some(StrSimplifiedType), - ty::Array(..) => Some(ArraySimplifiedType), - ty::Slice(..) => Some(SliceSimplifiedType), - ty::RawPtr(ptr) => Some(PtrSimplifiedType(ptr.mutbl)), + ty::Bool => Some(SimplifiedType::Bool), + ty::Char => Some(SimplifiedType::Char), + ty::Int(int_type) => Some(SimplifiedType::Int(int_type)), + ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)), + ty::Float(float_type) => Some(SimplifiedType::Float(float_type)), + ty::Adt(def, _) => Some(SimplifiedType::Adt(def.did())), + ty::Str => Some(SimplifiedType::Str), + ty::Array(..) => Some(SimplifiedType::Array), + ty::Slice(..) => Some(SimplifiedType::Slice), + ty::RawPtr(ptr) => Some(SimplifiedType::Ptr(ptr.mutbl)), ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() { Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => { - Some(TraitSimplifiedType(principal_def_id)) + Some(SimplifiedType::Trait(principal_def_id)) } - _ => Some(MarkerTraitObjectSimplifiedType), + _ => Some(SimplifiedType::MarkerTraitObject), }, - ty::Ref(_, _, mutbl) => Some(RefSimplifiedType(mutbl)), - ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(ClosureSimplifiedType(def_id)), - ty::Generator(def_id, _, _) => Some(GeneratorSimplifiedType(def_id)), - ty::GeneratorWitness(tys) => Some(GeneratorWitnessSimplifiedType(tys.skip_binder().len())), - ty::GeneratorWitnessMIR(def_id, _) => Some(GeneratorWitnessMIRSimplifiedType(def_id)), - ty::Never => Some(NeverSimplifiedType), - ty::Tuple(tys) => Some(TupleSimplifiedType(tys.len())), - ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())), - ty::Placeholder(..) => Some(PlaceholderSimplifiedType), + ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)), + ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(SimplifiedType::Closure(def_id)), + ty::Generator(def_id, _, _) => Some(SimplifiedType::Generator(def_id)), + ty::GeneratorWitness(tys) => { + Some(SimplifiedType::GeneratorWitness(tys.skip_binder().len())) + } + ty::GeneratorWitnessMIR(def_id, _) => Some(SimplifiedType::GeneratorWitnessMIR(def_id)), + ty::Never => Some(SimplifiedType::Never), + ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())), + ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())), + ty::Placeholder(..) => Some(SimplifiedType::Placeholder), ty::Param(_) => match treat_params { TreatParams::ForLookup | TreatParams::NextSolverLookup => { - Some(PlaceholderSimplifiedType) + Some(SimplifiedType::Placeholder) } TreatParams::AsCandidateKey => None, }, @@ -147,11 +150,13 @@ pub fn simplify_type<'tcx>( // // We will have to be careful with lazy normalization here. // FIXME(lazy_normalization): This is probably not right... - TreatParams::ForLookup if !ty.has_non_region_infer() => Some(PlaceholderSimplifiedType), - TreatParams::NextSolverLookup => Some(PlaceholderSimplifiedType), + TreatParams::ForLookup if !ty.has_non_region_infer() => { + Some(SimplifiedType::Placeholder) + } + TreatParams::NextSolverLookup => Some(SimplifiedType::Placeholder), TreatParams::ForLookup | TreatParams::AsCandidateKey => None, }, - ty::Foreign(def_id) => Some(ForeignSimplifiedType(def_id)), + ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), ty::Bound(..) | ty::Infer(_) | ty::Error(_) => None, } } @@ -159,12 +164,12 @@ pub fn simplify_type<'tcx>( impl SimplifiedType { pub fn def(self) -> Option<DefId> { match self { - AdtSimplifiedType(d) - | ForeignSimplifiedType(d) - | TraitSimplifiedType(d) - | ClosureSimplifiedType(d) - | GeneratorSimplifiedType(d) - | GeneratorWitnessMIRSimplifiedType(d) => Some(d), + SimplifiedType::Adt(d) + | SimplifiedType::Foreign(d) + | SimplifiedType::Trait(d) + | SimplifiedType::Closure(d) + | SimplifiedType::Generator(d) + | SimplifiedType::GeneratorWitnessMIR(d) => Some(d), _ => None, } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 48e88daa890..8913bf76d34 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -308,13 +308,13 @@ fn fmt_instance( InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"), InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"), InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"), - InstanceDef::Virtual(_, num) => write!(f, " - virtual#{}", num), - InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({})", ty), + InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"), + InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({ty})"), InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"), InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"), - InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty), - InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty), - InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({})", ty), + InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), + InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"), + InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), } } @@ -336,9 +336,7 @@ impl<'tcx> Instance<'tcx> { pub fn new(def_id: DefId, args: GenericArgsRef<'tcx>) -> Instance<'tcx> { assert!( !args.has_escaping_bound_vars(), - "args of instance {:?} not normalized for codegen: {:?}", - def_id, - args + "args of instance {def_id:?} not normalized for codegen: {args:?}" ); Instance { def: InstanceDef::Item(def_id), args } } @@ -425,7 +423,7 @@ impl<'tcx> Instance<'tcx> { ) -> Option<Instance<'tcx>> { debug!("resolve(def_id={:?}, args={:?})", def_id, args); // Use either `resolve_closure` or `resolve_for_vtable` - assert!(!tcx.is_closure(def_id), "Called `resolve_for_fn_ptr` on closure: {:?}", def_id); + assert!(!tcx.is_closure(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}"); Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| { match resolved.def { InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 62805d1e8b5..0f7bed0845c 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::DefId; use rustc_index::IndexVec; use rustc_session::config::OptLevel; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_target::abi::call::FnAbi; use rustc_target::abi::*; use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target}; @@ -212,6 +212,7 @@ pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), SizeOverflow(Ty<'tcx>), NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), + ReferencesError(ErrorGuaranteed), Cycle, } @@ -224,6 +225,7 @@ impl<'tcx> LayoutError<'tcx> { SizeOverflow(_) => middle_values_too_big, NormalizationFailure(_, _) => middle_cannot_be_normalized, Cycle => middle_cycle, + ReferencesError(_) => middle_layout_references_error, } } @@ -237,6 +239,7 @@ impl<'tcx> LayoutError<'tcx> { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } } Cycle => E::Cycle, + ReferencesError(_) => E::ReferencesError, } } } @@ -246,9 +249,9 @@ impl<'tcx> LayoutError<'tcx> { impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - LayoutError::Unknown(ty) => write!(f, "the type `{}` has an unknown layout", ty), + LayoutError::Unknown(ty) => write!(f, "the type `{ty}` has an unknown layout"), LayoutError::SizeOverflow(ty) => { - write!(f, "values of the type `{}` are too big for the current architecture", ty) + write!(f, "values of the type `{ty}` are too big for the current architecture") } LayoutError::NormalizationFailure(t, e) => write!( f, @@ -257,6 +260,7 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { e.get_type_for_failure() ), LayoutError::Cycle => write!(f, "a cycle occurred during layout computation"), + LayoutError::ReferencesError(_) => write!(f, "the type has an unknown layout"), } } } @@ -323,7 +327,8 @@ impl<'tcx> SizeSkeleton<'tcx> { Err( e @ LayoutError::Cycle | e @ LayoutError::SizeOverflow(_) - | e @ LayoutError::NormalizationFailure(..), + | e @ LayoutError::NormalizationFailure(..) + | e @ LayoutError::ReferencesError(_), ) => return Err(e), }; @@ -741,9 +746,9 @@ where let fields = match this.ty.kind() { ty::Adt(def, _) if def.variants().is_empty() => - bug!("for_variant called on zero-variant enum"), + bug!("for_variant called on zero-variant enum {}", this.ty), ty::Adt(def, _) => def.variant(variant_index).fields.len(), - _ => bug!(), + _ => bug!("`ty_and_layout_for_variant` on unexpected type {}", this.ty), }; tcx.mk_layout(LayoutS { variants: Variants::Single { index: variant_index }, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0411890ab51..2b4c834f766 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -867,20 +867,6 @@ pub struct TraitPredicate<'tcx> { pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; impl<'tcx> TraitPredicate<'tcx> { - pub fn remap_constness(&mut self, param_env: &mut ParamEnv<'tcx>) { - *param_env = param_env.with_constness(self.constness.and(param_env.constness())) - } - - /// Remap the constness of this predicate before emitting it for diagnostics. - pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) { - // this is different to `remap_constness` that callees want to print this predicate - // in case of selection errors. `T: ~const Drop` bounds cannot end up here when the - // param_env is not const because it is always satisfied in non-const contexts. - if let hir::Constness::NotConst = param_env.constness() { - self.constness = ty::BoundConstness::NotConst; - } - } - pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { Self { trait_ref: self.trait_ref.with_self_ty(tcx, self_ty), ..self } } @@ -922,14 +908,6 @@ impl<'tcx> PolyTraitPredicate<'tcx> { self.map_bound(|trait_ref| trait_ref.self_ty()) } - /// Remap the constness of this predicate before emitting it for diagnostics. - pub fn remap_constness_diag(&mut self, param_env: ParamEnv<'tcx>) { - *self = self.map_bound(|mut p| { - p.remap_constness_diag(param_env); - p - }); - } - #[inline] pub fn is_const_if_const(self) -> bool { self.skip_binder().is_const_if_const() @@ -980,9 +958,9 @@ pub struct Term<'tcx> { impl Debug for Term<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let data = if let Some(ty) = self.ty() { - format!("Term::Ty({:?})", ty) + format!("Term::Ty({ty:?})") } else if let Some(ct) = self.ct() { - format!("Term::Ct({:?})", ct) + format!("Term::Ct({ct:?})") } else { unreachable!() }; @@ -1381,12 +1359,24 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyTraitPredicate<'tcx> { } } +impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(self))).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).to_predicate(tcx) } } +impl<'tcx> ToPredicate<'tcx> for OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(self))).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(ClauseKind::TypeOutlives(p))).to_predicate(tcx) @@ -1700,15 +1690,12 @@ pub struct ParamEnv<'tcx> { #[derive(Copy, Clone)] struct ParamTag { reveal: traits::Reveal, - constness: hir::Constness, } impl_tag! { impl Tag for ParamTag; - ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::NotConst }, - ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::NotConst }, - ParamTag { reveal: traits::Reveal::UserFacing, constness: hir::Constness::Const }, - ParamTag { reveal: traits::Reveal::All, constness: hir::Constness::Const }, + ParamTag { reveal: traits::Reveal::UserFacing }, + ParamTag { reveal: traits::Reveal::All }, } impl<'tcx> fmt::Debug for ParamEnv<'tcx> { @@ -1716,7 +1703,6 @@ impl<'tcx> fmt::Debug for ParamEnv<'tcx> { f.debug_struct("ParamEnv") .field("caller_bounds", &self.caller_bounds()) .field("reveal", &self.reveal()) - .field("constness", &self.constness()) .finish() } } @@ -1725,7 +1711,6 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ParamEnv<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { self.caller_bounds().hash_stable(hcx, hasher); self.reveal().hash_stable(hcx, hasher); - self.constness().hash_stable(hcx, hasher); } } @@ -1737,7 +1722,6 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ParamEnv<'tcx> { Ok(ParamEnv::new( self.caller_bounds().try_fold_with(folder)?, self.reveal().try_fold_with(folder)?, - self.constness(), )) } } @@ -1756,7 +1740,7 @@ impl<'tcx> ParamEnv<'tcx> { /// type-checking. #[inline] pub fn empty() -> Self { - Self::new(List::empty(), Reveal::UserFacing, hir::Constness::NotConst) + Self::new(List::empty(), Reveal::UserFacing) } #[inline] @@ -1769,16 +1753,6 @@ impl<'tcx> ParamEnv<'tcx> { self.packed.tag().reveal } - #[inline] - pub fn constness(self) -> hir::Constness { - self.packed.tag().constness - } - - #[inline] - pub fn is_const(self) -> bool { - self.packed.tag().constness == hir::Constness::Const - } - /// Construct a trait environment with no where-clauses in scope /// where the values of all `impl Trait` and other hidden types /// are revealed. This is suitable for monomorphized, post-typeck @@ -1788,17 +1762,13 @@ impl<'tcx> ParamEnv<'tcx> { /// or invoke `param_env.with_reveal_all()`. #[inline] pub fn reveal_all() -> Self { - Self::new(List::empty(), Reveal::All, hir::Constness::NotConst) + Self::new(List::empty(), Reveal::All) } /// Construct a trait environment with the given set of predicates. #[inline] - pub fn new( - caller_bounds: &'tcx List<Clause<'tcx>>, - reveal: Reveal, - constness: hir::Constness, - ) -> Self { - ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal, constness }) } + pub fn new(caller_bounds: &'tcx List<Clause<'tcx>>, reveal: Reveal) -> Self { + ty::ParamEnv { packed: CopyTaggedPtr::new(caller_bounds, ParamTag { reveal }) } } pub fn with_user_facing(mut self) -> Self { @@ -1806,29 +1776,6 @@ impl<'tcx> ParamEnv<'tcx> { self } - #[inline] - pub fn with_constness(mut self, constness: hir::Constness) -> Self { - self.packed.set_tag(ParamTag { constness, ..self.packed.tag() }); - self - } - - #[inline] - pub fn with_const(mut self) -> Self { - self.packed.set_tag(ParamTag { constness: hir::Constness::Const, ..self.packed.tag() }); - self - } - - #[inline] - pub fn without_const(mut self) -> Self { - self.packed.set_tag(ParamTag { constness: hir::Constness::NotConst, ..self.packed.tag() }); - self - } - - #[inline] - pub fn remap_constness_with(&mut self, mut constness: ty::BoundConstness) { - *self = self.with_constness(constness.and(self.constness())) - } - /// Returns a new parameter environment with the same clauses, but /// which "reveals" the true results of projections in all cases /// (even for associated types that are specializable). This is @@ -1843,17 +1790,13 @@ impl<'tcx> ParamEnv<'tcx> { return self; } - ParamEnv::new( - tcx.reveal_opaque_types_in_bounds(self.caller_bounds()), - Reveal::All, - self.constness(), - ) + ParamEnv::new(tcx.reveal_opaque_types_in_bounds(self.caller_bounds()), Reveal::All) } /// Returns this same environment but with no caller bounds. #[inline] pub fn without_caller_bounds(self) -> Self { - Self::new(List::empty(), self.reveal(), self.constness()) + Self::new(List::empty(), self.reveal()) } /// Creates a suitable environment in which to perform trait @@ -2682,19 +2625,6 @@ impl<'tcx> TyCtxt<'tcx> { matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait)) } - pub fn impl_trait_in_trait_parent_fn(self, mut def_id: DefId) -> DefId { - match self.opt_rpitit_info(def_id) { - Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) - | Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) => fn_def_id, - None => { - while let def_kind = self.def_kind(def_id) && def_kind != DefKind::AssocFn { - def_id = self.parent(def_id); - } - def_id - } - } - } - /// Returns the `DefId` of the item within which the `impl Trait` is declared. /// For type-alias-impl-trait this is the `type` alias. /// For impl-trait-in-assoc-type this is the assoc type. diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 3c2c4483c73..2415d50b278 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -20,8 +20,8 @@ pub enum NormalizationError<'tcx> { impl<'tcx> NormalizationError<'tcx> { pub fn get_type_for_failure(&self) -> String { match self { - NormalizationError::Type(t) => format!("{}", t), - NormalizationError::Const(c) => format!("{}", c), + NormalizationError::Type(t) => format!("{t}"), + NormalizationError::Const(c) => format!("{c}"), } } } diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs index 7ae8be2dab3..0ff5ac90304 100644 --- a/compiler/rustc_middle/src/ty/opaque_types.rs +++ b/compiler/rustc_middle/src/ty/opaque_types.rs @@ -124,7 +124,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { match self.map.get(&r.into()).map(|k| k.unpack()) { Some(GenericArgKind::Lifetime(r1)) => r1, - Some(u) => panic!("region mapped to unexpected kind: {:?}", u), + Some(u) => panic!("region mapped to unexpected kind: {u:?}"), None if self.do_not_error => self.tcx.lifetimes.re_static, None => { let e = self @@ -134,9 +134,8 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { .span_label( self.span, format!( - "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", - r + "lifetime `{r}` is part of concrete type but not used in \ + parameter list of the `impl Trait` type alias" ), ) .emit(); @@ -169,7 +168,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { // Found it in the substitution list; replace with the parameter from the // opaque type. Some(GenericArgKind::Type(t1)) => t1, - Some(u) => panic!("type mapped to unexpected kind: {:?}", u), + Some(u) => panic!("type mapped to unexpected kind: {u:?}"), None => { debug!(?param, ?self.map); if !self.ignore_errors { @@ -178,9 +177,8 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { .struct_span_err( self.span, format!( - "type parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ty + "type parameter `{ty}` is part of concrete type but not \ + used in parameter list for the `impl Trait` type alias" ), ) .emit(); @@ -205,7 +203,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> { // Found it in the substitution list, replace with the parameter from the // opaque type. Some(GenericArgKind::Const(c1)) => c1, - Some(u) => panic!("const mapped to unexpected kind: {:?}", u), + Some(u) => panic!("const mapped to unexpected kind: {u:?}"), None => { let guar = self .tcx diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 70eb389b406..f146f8aa8b4 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -17,6 +17,7 @@ use rustc_hir::LangItem; use rustc_session::config::TrimmedDefPaths; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_session::Limit; +use rustc_span::sym; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::FileNameDisplayPreference; use rustc_target::abi::Size; @@ -1497,7 +1498,7 @@ pub trait PrettyPrinter<'tcx>: let data = int.assert_bits(self.tcx().data_layout.pointer_size); self = self.typed_value( |mut this| { - write!(this, "0x{:x}", data)?; + write!(this, "0x{data:x}")?; Ok(this) }, |this| this.print_type(ty), @@ -1510,7 +1511,7 @@ pub trait PrettyPrinter<'tcx>: if int.size() == Size::ZERO { write!(this, "transmute(())")?; } else { - write!(this, "transmute(0x{:x})", int)?; + write!(this, "transmute(0x{int:x})")?; } Ok(this) }; @@ -1932,7 +1933,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> { self.empty_path = true; if cnum == LOCAL_CRATE { - if self.tcx.sess.rust_2018() { + if self.tcx.sess.at_least_rust_2018() { // We add the `crate::` keyword on Rust 2018, only when desired. if SHOULD_PREFIX_WITH_CRATE.with(|flag| flag.get()) { write!(self, "{}", kw::Crate)?; @@ -2017,11 +2018,37 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { ) -> Result<Self::Path, Self::Error> { self = print_prefix(self)?; - if args.first().is_some() { + let tcx = self.tcx; + + let args = args.iter().copied(); + + let args: Vec<_> = if !tcx.sess.verbose() { + // skip host param as those are printed as `~const` + args.filter(|arg| match arg.unpack() { + // FIXME(effects) there should be a better way than just matching the name + GenericArgKind::Const(c) + if tcx.features().effects + && matches!( + c.kind(), + ty::ConstKind::Param(ty::ParamConst { name: sym::host, .. }) + ) => + { + false + } + _ => true, + }) + .collect() + } else { + // If -Zverbose is passed, we should print the host parameter instead + // of eating it. + args.collect() + }; + + if !args.is_empty() { if self.in_value { write!(self, "::")?; } - self.generic_delimiters(|cx| cx.comma_sep(args.iter().cloned())) + self.generic_delimiters(|cx| cx.comma_sep(args.into_iter())) } else { Ok(self) } @@ -2348,10 +2375,10 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { } else { cont }; - let _ = write!(cx, "{}", w); + let _ = write!(cx, "{w}"); }; let do_continue = |cx: &mut Self, cont: Symbol| { - let _ = write!(cx, "{}", cont); + let _ = write!(cx, "{cont}"); }; define_scoped_cx!(self); @@ -2387,7 +2414,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { let (new_value, map) = if self.should_print_verbose() { for var in value.bound_vars().iter() { start_or_continue(&mut self, "for<", ", "); - write!(self, "{:?}", var)?; + write!(self, "{var:?}")?; } start_or_continue(&mut self, "", "> "); (value.clone().skip_binder(), BTreeMap::default()) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 9d380a58d9f..c6b6f0e8990 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -73,9 +73,9 @@ impl fmt::Debug for ty::BoundRegionKind { ty::BrAnon(span) => write!(f, "BrAnon({span:?})"), ty::BrNamed(did, name) => { if did.is_crate_root() { - write!(f, "BrNamed({})", name) + write!(f, "BrNamed({name})") } else { - write!(f, "BrNamed({:?}, {})", did, name) + write!(f, "BrNamed({did:?}, {name})") } } ty::BrEnv => write!(f, "BrEnv"), @@ -205,7 +205,7 @@ impl<'tcx> fmt::Debug for ty::ClauseKind<'tcx> { ty::ClauseKind::RegionOutlives(ref pair) => pair.fmt(f), ty::ClauseKind::TypeOutlives(ref pair) => pair.fmt(f), ty::ClauseKind::Projection(ref pair) => pair.fmt(f), - ty::ClauseKind::WellFormed(ref data) => write!(f, "WellFormed({:?})", data), + ty::ClauseKind::WellFormed(ref data) => write!(f, "WellFormed({data:?})"), ty::ClauseKind::ConstEvaluatable(ct) => { write!(f, "ConstEvaluatable({ct:?})") } @@ -220,12 +220,12 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), ty::PredicateKind::Coerce(ref pair) => pair.fmt(f), ty::PredicateKind::ObjectSafe(trait_def_id) => { - write!(f, "ObjectSafe({:?})", trait_def_id) + write!(f, "ObjectSafe({trait_def_id:?})") } ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { - write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_args, kind) + write!(f, "ClosureKind({closure_def_id:?}, {closure_args:?}, {kind:?})") } - ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2), + ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"), ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"), ty::PredicateKind::AliasRelate(t1, t2, dir) => { write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") @@ -470,10 +470,8 @@ TrivialTypeTraversalAndLiftImpls! { ::rustc_hir::Unsafety, ::rustc_target::asm::InlineAsmRegOrRegClass, ::rustc_target::spec::abi::Abi, - crate::mir::coverage::ExpressionOperandId, - crate::mir::coverage::CounterValueReference, - crate::mir::coverage::InjectedExpressionId, - crate::mir::coverage::InjectedExpressionIndex, + crate::mir::coverage::CounterId, + crate::mir::coverage::ExpressionId, crate::mir::coverage::MappedExpressionIndex, crate::mir::Local, crate::mir::Promoted, @@ -602,7 +600,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { type Lifted = ty::ParamEnv<'tcx>; fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> { tcx.lift(self.caller_bounds()) - .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.constness())) + .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal())) } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index cdb0b2240a4..3e023ccdead 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -15,7 +15,6 @@ use hir::def::DefKind; use polonius_engine::Atom; use rustc_data_structures::captures::Captures; use rustc_data_structures::intern::Interned; -use rustc_error_messages::DiagnosticMessage; use rustc_errors::{DiagnosticArgValue, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -1991,7 +1990,7 @@ impl<'tcx> Ty<'tcx> { pub fn new_error_with_message<S: Into<MultiSpan>>( tcx: TyCtxt<'tcx>, span: S, - msg: impl Into<DiagnosticMessage>, + msg: impl Into<String>, ) -> Ty<'tcx> { let reported = tcx.sess.delay_span_bug(span, msg); Ty::new(tcx, Error(reported)) @@ -2671,11 +2670,6 @@ impl<'tcx> Ty<'tcx> { variant_index: VariantIdx, ) -> Option<Discr<'tcx>> { match self.kind() { - TyKind::Adt(adt, _) if adt.variants().is_empty() => { - // This can actually happen during CTFE, see - // https://github.com/rust-lang/rust/issues/89765. - None - } TyKind::Adt(adt, _) if adt.is_enum() => { Some(adt.discriminant_for_variant(tcx, variant_index)) } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index b4852ab8881..90ecc8aa857 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -19,7 +19,7 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_macros::HashStable; use rustc_session::Limit; use rustc_span::sym; -use rustc_target::abi::{Integer, IntegerType, Size, TargetDataLayout}; +use rustc_target::abi::{Integer, IntegerType, Size}; use rustc_target::spec::abi::Abi; use smallvec::SmallVec; use std::{fmt, iter}; @@ -57,7 +57,7 @@ impl<'tcx> fmt::Display for Discr<'tcx> { let x = self.val; // sign extend the raw representation to be an i128 let x = size.sign_extend(x) as i128; - write!(fmt, "{}", x) + write!(fmt, "{x}") } _ => write!(fmt, "{}", self.val), } @@ -1085,7 +1085,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn needs_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { // Avoid querying in simple cases. - match needs_drop_components(self, &tcx.data_layout) { + match needs_drop_components(tcx, self) { Err(AlwaysRequiresDrop) => true, Ok(components) => { let query_ty = match *components { @@ -1118,7 +1118,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn has_significant_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { // Avoid querying in simple cases. - match needs_drop_components(self, &tcx.data_layout) { + match needs_drop_components(tcx, self) { Err(AlwaysRequiresDrop) => true, Ok(components) => { let query_ty = match *components { @@ -1278,10 +1278,10 @@ impl<'tcx> ExplicitSelf<'tcx> { /// *any* of the returned types need drop. Returns `Err(AlwaysRequiresDrop)` if /// this type always needs drop. pub fn needs_drop_components<'tcx>( + tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - target_layout: &TargetDataLayout, ) -> Result<SmallVec<[Ty<'tcx>; 2]>, AlwaysRequiresDrop> { - match ty.kind() { + match *ty.kind() { ty::Infer(ty::FreshIntTy(_)) | ty::Infer(ty::FreshFloatTy(_)) | ty::Bool @@ -1303,11 +1303,11 @@ pub fn needs_drop_components<'tcx>( ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop), - ty::Slice(ty) => needs_drop_components(*ty, target_layout), + ty::Slice(ty) => needs_drop_components(tcx, ty), ty::Array(elem_ty, size) => { - match needs_drop_components(*elem_ty, target_layout) { + match needs_drop_components(tcx, elem_ty) { Ok(v) if v.is_empty() => Ok(v), - res => match size.try_to_bits(target_layout.pointer_size) { + res => match size.try_to_target_usize(tcx) { // Arrays of size zero don't need drop, even if their element // type does. Some(0) => Ok(SmallVec::new()), @@ -1321,7 +1321,7 @@ pub fn needs_drop_components<'tcx>( } // If any field needs drop, then the whole tuple does. ty::Tuple(fields) => fields.iter().try_fold(SmallVec::new(), move |mut acc, elem| { - acc.extend(needs_drop_components(elem, target_layout)?); + acc.extend(needs_drop_components(tcx, elem)?); Ok(acc) }), diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 443791d0af4..97402caa001 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -29,8 +29,8 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> { VtblEntry::MetadataSize => write!(f, "MetadataSize"), VtblEntry::MetadataAlign => write!(f, "MetadataAlign"), VtblEntry::Vacant => write!(f, "Vacant"), - VtblEntry::Method(instance) => write!(f, "Method({})", instance), - VtblEntry::TraitVPtr(trait_ref) => write!(f, "TraitVPtr({})", trait_ref), + VtblEntry::Method(instance) => write!(f, "Method({instance})"), + VtblEntry::TraitVPtr(trait_ref) => write!(f, "TraitVPtr({trait_ref})"), } } } diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index 43ee0343f5a..634ed5ec54b 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -29,7 +29,7 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>( location: &Location<'_>, ) -> ! { tls::with_opt(move |tcx| { - let msg = format!("{}: {}", location, args); + let msg = format!("{location}: {args}"); match (tcx, span) { (Some(tcx), Some(span)) => tcx.sess.diagnostic().span_bug(span, msg), (Some(tcx), None) => tcx.sess.diagnostic().bug(msg), diff --git a/compiler/rustc_middle/src/util/common.rs b/compiler/rustc_middle/src/util/common.rs index 08977049db0..df101a2f6e4 100644 --- a/compiler/rustc_middle/src/util/common.rs +++ b/compiler/rustc_middle/src/util/common.rs @@ -17,7 +17,7 @@ pub fn to_readable_str(mut val: usize) -> String { groups.push(group.to_string()); break; } else { - groups.push(format!("{:03}", group)); + groups.push(format!("{group:03}")); } } diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml index 58449ee9eb4..c7e2c625ce5 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_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs index c494929cbff..60c4a041696 100644 --- a/compiler/rustc_mir_build/src/build/custom/parse.rs +++ b/compiler/rustc_mir_build/src/build/custom/parse.rs @@ -74,7 +74,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> { kind @ StmtKind::Let { pattern, .. } => { return Err(ParseError { span: pattern.span, - item_description: format!("{:?}", kind), + item_description: format!("{kind:?}"), expected: "expression".to_string(), }); } diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index c122230b841..7756d5d4879 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -730,5 +730,5 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Precise capture is enabled if user is using Rust Edition 2021 or higher. fn enable_precise_capture(closure_span: Span) -> bool { - closure_span.rust_2021() + closure_span.at_least_rust_2021() } diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 7817c3d0d80..3220a184d49 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -774,8 +774,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Not in a closure debug_assert!( local == ty::CAPTURE_STRUCT_LOCAL, - "Expected local to be Local(1), found {:?}", - local + "Expected local to be Local(1), found {local:?}" ); // Not in a closure debug_assert!( diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 6f14891cb78..ed3ac7cb3ec 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1627,9 +1627,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // at least the first candidate ought to be tested assert!( total_candidate_count > candidates.len(), - "{}, {:#?}", - total_candidate_count, - candidates + "{total_candidate_count}, {candidates:#?}" ); debug!("tested_candidates: {}", total_candidate_count - candidates.len()); debug!("untested_candidates: {}", candidates.len()); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 275878a3919..484e8490919 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -175,16 +175,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug_assert_ne!( target_blocks[idx.index()], otherwise_block, - "no candidates for tested discriminant: {:?}", - discr, + "no candidates for tested discriminant: {discr:?}", ); Some((discr.val, target_blocks[idx.index()])) } else { debug_assert_eq!( target_blocks[idx.index()], otherwise_block, - "found candidates for untested discriminant: {:?}", - discr, + "found candidates for untested discriminant: {discr:?}", ); None } diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 17295fe1d34..c66eba5520e 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -94,8 +94,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { || body.basic_blocks.has_free_regions() || body.var_debug_info.has_free_regions() || body.yield_ty().has_free_regions()), - "Unexpected free regions in MIR: {:?}", - body, + "Unexpected free regions in MIR: {body:?}", ); body @@ -571,7 +570,7 @@ fn construct_const<'a, 'tcx>( // Figure out what primary body this item has. let (span, const_ty_span) = match tcx.hir().get(hir_id) { Node::Item(hir::Item { - kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _), + kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _, _), span, .. }) @@ -977,9 +976,9 @@ pub(crate) fn parse_float_into_scalar( match float_ty { ty::FloatTy::F32 => { let Ok(rust_f) = num.parse::<f32>() else { return None }; - let mut f = num.parse::<Single>().unwrap_or_else(|e| { - panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e) - }); + let mut f = num + .parse::<Single>() + .unwrap_or_else(|e| panic!("apfloat::ieee::Single failed to parse `{num}`: {e:?}")); assert!( u128::from(rust_f.to_bits()) == f.to_bits(), @@ -1000,9 +999,9 @@ pub(crate) fn parse_float_into_scalar( } ty::FloatTy::F64 => { let Ok(rust_f) = num.parse::<f64>() else { return None }; - let mut f = num.parse::<Double>().unwrap_or_else(|e| { - panic!("apfloat::ieee::Double failed to parse `{}`: {:?}", num, e) - }); + let mut f = num + .parse::<Double>() + .unwrap_or_else(|e| panic!("apfloat::ieee::Double failed to parse `{num}`: {e:?}")); assert!( u128::from(rust_f.to_bits()) == f.to_bits(), diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 820ab93851e..3ff3387a781 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -454,17 +454,13 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> { if self.span.eq_ctxt(self.expr_span) { // Get the span for the empty match body `{}`. let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) { - (format!("\n{}", snippet), " ") + (format!("\n{snippet}"), " ") } else { (" ".to_string(), "") }; suggestion = Some(( self.span.shrink_to_hi().with_hi(self.expr_span.hi()), - format!( - " {{{indentation}{more}_ => todo!(),{indentation}}}", - indentation = indentation, - more = more, - ), + format!(" {{{indentation}{more}_ => todo!(),{indentation}}}",), )); } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 711a9126c04..ff4620948fa 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -229,9 +229,7 @@ impl<'tcx> Cx<'tcx> { let param_env_ty = self.param_env.and(discr_ty); let size = tcx .layout_of(param_env_ty) - .unwrap_or_else(|e| { - panic!("could not compute layout for {:?}: {:?}", param_env_ty, e) - }) + .unwrap_or_else(|e| panic!("could not compute layout for {param_env_ty:?}: {e:?}")) .size; let lit = ScalarInt::try_from_uint(discr_offset as u128, size).unwrap(); diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 38c866c50a2..a786e659664 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -691,7 +691,7 @@ fn non_exhaustive_match<'p, 'tcx>( err = create_e0004( cx.tcx.sess, sp, - format!("non-exhaustive patterns: {} not covered", joined_patterns), + format!("non-exhaustive patterns: {joined_patterns} not covered"), ); err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); patterns_len = witnesses.len(); @@ -721,15 +721,13 @@ fn non_exhaustive_match<'p, 'tcx>( && matches!(witnesses[0].ctor(), Constructor::NonExhaustive) { err.note(format!( - "`{}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \ + "`{scrut_ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \ exhaustively", - scrut_ty, )); if cx.tcx.sess.is_nightly_build() { err.help(format!( "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \ - enable precise `{}` matching", - scrut_ty, + enable precise `{scrut_ty}` matching", )); } } @@ -745,18 +743,13 @@ fn non_exhaustive_match<'p, 'tcx>( [] if sp.eq_ctxt(expr_span) => { // Get the span for the empty match body `{}`. let (indentation, more) = if let Some(snippet) = sm.indentation_before(sp) { - (format!("\n{}", snippet), " ") + (format!("\n{snippet}"), " ") } else { (" ".to_string(), "") }; suggestion = Some(( sp.shrink_to_hi().with_hi(expr_span.hi()), - format!( - " {{{indentation}{more}{pattern} => todo!(),{indentation}}}", - indentation = indentation, - more = more, - pattern = pattern, - ), + format!(" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",), )); } [only] => { @@ -765,7 +758,7 @@ fn non_exhaustive_match<'p, 'tcx>( && let Ok(with_trailing) = sm.span_extend_while(only.span, |c| c.is_whitespace() || c == ',') && sm.is_multiline(with_trailing) { - (format!("\n{}", snippet), true) + (format!("\n{snippet}"), true) } else { (" ".to_string(), false) }; @@ -780,7 +773,7 @@ fn non_exhaustive_match<'p, 'tcx>( }; suggestion = Some(( only.span.shrink_to_hi(), - format!("{}{}{} => todo!()", comma, pre_indentation, pattern), + format!("{comma}{pre_indentation}{pattern} => todo!()"), )); } [.., prev, last] => { @@ -803,7 +796,7 @@ fn non_exhaustive_match<'p, 'tcx>( if let Some(spacing) = spacing { suggestion = Some(( last.span.shrink_to_hi(), - format!("{}{}{} => todo!()", comma, spacing, pattern), + format!("{comma}{spacing}{pattern} => todo!()"), )); } } @@ -900,7 +893,7 @@ fn adt_defined_here<'p, 'tcx>( for pat in spans { span.push_span_label(pat, "not covered"); } - err.span_note(span, format!("`{}` defined here", ty)); + err.span_note(span, format!("`{ty}` defined here")); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 4228597cdc9..bee1c4e4614 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -306,9 +306,9 @@ impl fmt::Debug for IntRange { let (lo, hi) = self.boundaries(); let bias = self.bias; let (lo, hi) = (lo ^ bias, hi ^ bias); - write!(f, "{}", lo)?; + write!(f, "{lo}")?; write!(f, "{}", RangeEnd::Included)?; - write!(f, "{}", hi) + write!(f, "{hi}") } } @@ -1619,7 +1619,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { // of `std`). So this branch is only reachable when the feature is enabled and // the pattern is a box pattern. let subpattern = self.iter_fields().next().unwrap(); - write!(f, "box {:?}", subpattern) + write!(f, "box {subpattern:?}") } ty::Adt(..) | ty::Tuple(..) => { let variant = match self.ty.kind() { @@ -1638,7 +1638,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { write!(f, "(")?; for p in self.iter_fields() { write!(f, "{}", start_or_comma())?; - write!(f, "{:?}", p)?; + write!(f, "{p:?}")?; } write!(f, ")") } @@ -1674,11 +1674,11 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { write!(f, "]") } &FloatRange(lo, hi, end) => { - write!(f, "{}", lo)?; - write!(f, "{}", end)?; - write!(f, "{}", hi) + write!(f, "{lo}")?; + write!(f, "{end}")?; + write!(f, "{hi}") } - IntRange(range) => write!(f, "{:?}", range), // Best-effort, will render e.g. `false` as `0..=0` + IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0` Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty), Or => { for pat in self.iter_fields() { @@ -1686,7 +1686,7 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> { } Ok(()) } - Str(value) => write!(f, "{}", value), + Str(value) => write!(f, "{value}"), Opaque => write!(f, "<constant pattern>"), } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index e5b63506906..08cfe98bb68 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -459,7 +459,7 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "+")?; for pat in self.iter() { - write!(f, " {:?} +", pat)?; + write!(f, " {pat:?} +")?; } Ok(()) } @@ -530,7 +530,7 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { let Matrix { patterns: m, .. } = self; let pretty_printed_matrix: Vec<Vec<String>> = - m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect(); + m.iter().map(|row| row.iter().map(|pat| format!("{pat:?}")).collect()).collect(); let column_count = m.iter().map(|row| row.len()).next().unwrap_or(0); assert!(m.iter().all(|row| row.len() == column_count)); diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 9662c19777f..34e0834a68b 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -88,7 +88,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } } -struct TransferFunction<'a, T>(&'a mut T); +pub struct TransferFunction<'a, T>(pub &'a mut T); impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T> where diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 633b99a332b..7ddd01e34aa 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -26,6 +26,7 @@ pub use self::borrowed_locals::borrowed_locals; pub use self::borrowed_locals::MaybeBorrowedLocals; pub use self::liveness::MaybeLiveLocals; pub use self::liveness::MaybeTransitiveLiveLocals; +pub use self::liveness::TransferFunction as LivenessTransferFunction; pub use self::storage_liveness::{MaybeRequiresStorage, MaybeStorageDead, MaybeStorageLive}; /// `MaybeInitializedPlaces` tracks all places that might be diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index fd6b1dc5c07..8d78ec04821 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -1099,10 +1099,10 @@ fn debug_with_context_rec<V: Debug + Eq>( let info_elem = map.places[child].proj_elem.unwrap(); let child_place_str = match info_elem { TrackElem::Discriminant => { - format!("discriminant({})", place_str) + format!("discriminant({place_str})") } TrackElem::Variant(idx) => { - format!("({} as {:?})", place_str, idx) + format!("({place_str} as {idx:?})") } TrackElem::Field(field) => { if place_str.starts_with('*') { diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 6437a227d47..7529ed8186b 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -14,8 +14,7 @@ use rustc_middle::mir::visit::{ }; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; -use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, GenericArgs, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{def_id::DefId, Span, DUMMY_SP}; use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout}; use rustc_target::spec::abi::Abi as CallAbi; @@ -407,51 +406,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { ecx.machine.written_only_inside_own_block_locals.remove(&local); } - /// Returns the value, if any, of evaluating `c`. - fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<OpTy<'tcx>> { - // FIXME we need to revisit this for #67176 - if c.has_param() { - return None; - } - - // No span, we don't want errors to be shown. - self.ecx.eval_mir_constant(&c.literal, None, None).ok() - } - - /// Returns the value, if any, of evaluating `place`. - fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> { - trace!("eval_place(place={:?})", place); - self.ecx.eval_place_to_op(place, None).ok() - } - - /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` - /// or `eval_place`, depending on the variant of `Operand` used. - fn eval_operand(&mut self, op: &Operand<'tcx>) -> Option<OpTy<'tcx>> { - match *op { - Operand::Constant(ref c) => self.eval_constant(c), - Operand::Move(place) | Operand::Copy(place) => self.eval_place(place), - } - } - fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) { - match *operand { - Operand::Copy(l) | Operand::Move(l) => { - if let Some(value) = self.get_const(l) && self.should_const_prop(&value) { - // FIXME(felix91gr): this code only handles `Scalar` cases. - // For now, we're not handling `ScalarPair` cases because - // doing so here would require a lot of code duplication. - // We should hopefully generalize `Operand` handling into a fn, - // and use it to do const-prop here and everywhere else - // where it makes sense. - if let interpret::Operand::Immediate(interpret::Immediate::Scalar( - scalar, - )) = *value - { - *operand = self.operand_from_scalar(scalar, value.layout.ty); - } - } - } - Operand::Constant(_) => (), + if let Some(place) = operand.place() && let Some(op) = self.replace_with_const(place) { + *operand = op; } } @@ -579,93 +536,45 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { })) } - fn replace_with_const(&mut self, place: Place<'tcx>, rval: &mut Rvalue<'tcx>) { + fn replace_with_const(&mut self, place: Place<'tcx>) -> Option<Operand<'tcx>> { // This will return None if the above `const_prop` invocation only "wrote" a // type whose creation requires no write. E.g. a generator whose initial state // consists solely of uninitialized memory (so it doesn't capture any locals). - let Some(ref value) = self.get_const(place) else { return }; - if !self.should_const_prop(value) { - return; - } - trace!("replacing {:?}={:?} with {:?}", place, rval, value); - - if let Rvalue::Use(Operand::Constant(c)) = rval { - match c.literal { - ConstantKind::Ty(c) if matches!(c.kind(), ConstKind::Unevaluated(..)) => {} - _ => { - trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); - return; - } - } + let value = self.get_const(place)?; + if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - {value:?}")) { + return None; } + trace!("replacing {:?} with {:?}", place, value); - trace!("attempting to replace {:?} with {:?}", rval, value); // FIXME> figure out what to do when read_immediate_raw fails - let imm = self.ecx.read_immediate_raw(value).ok(); + let imm = self.ecx.read_immediate_raw(&value).ok()?; - if let Some(Right(imm)) = imm { - match *imm { - interpret::Immediate::Scalar(scalar) => { - *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty)); - } - Immediate::ScalarPair(..) => { - // Found a value represented as a pair. For now only do const-prop if the type - // of `rvalue` is also a tuple with two scalars. - // FIXME: enable the general case stated above ^. - let ty = value.layout.ty; - // Only do it for tuples - if let ty::Tuple(types) = ty.kind() { - // Only do it if tuple is also a pair with two scalars - if let [ty1, ty2] = types[..] { - let ty_is_scalar = |ty| { - self.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar()) - == Some(true) - }; - let alloc = if ty_is_scalar(ty1) && ty_is_scalar(ty2) { - let alloc = self - .ecx - .intern_with_temp_alloc(value.layout, |ecx, dest| { - ecx.write_immediate(*imm, dest) - }) - .unwrap(); - Some(alloc) - } else { - None - }; - - if let Some(alloc) = alloc { - // Assign entire constant in a single statement. - // We can't use aggregates, as we run after the aggregate-lowering `MirPhase`. - let const_val = ConstValue::ByRef { alloc, offset: Size::ZERO }; - let literal = ConstantKind::Val(const_val, ty); - *rval = Rvalue::Use(Operand::Constant(Box::new(Constant { - span: DUMMY_SP, - user_ty: None, - literal, - }))); - } - } - } - } - // Scalars or scalar pairs that contain undef values are assumed to not have - // successfully evaluated and are thus not propagated. - _ => {} + let Right(imm) = imm else { return None }; + match *imm { + Immediate::Scalar(scalar) if scalar.try_to_int().is_ok() => { + Some(self.operand_from_scalar(scalar, value.layout.ty)) } - } - } - - /// Returns `true` if and only if this `op` should be const-propagated into. - fn should_const_prop(&mut self, op: &OpTy<'tcx>) -> bool { - if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - OpTy: {:?}", op)) { - return false; - } - - match **op { - interpret::Operand::Immediate(Immediate::Scalar(s)) => s.try_to_int().is_ok(), - interpret::Operand::Immediate(Immediate::ScalarPair(l, r)) => { - l.try_to_int().is_ok() && r.try_to_int().is_ok() + Immediate::ScalarPair(l, r) if l.try_to_int().is_ok() && r.try_to_int().is_ok() => { + let alloc = self + .ecx + .intern_with_temp_alloc(value.layout, |ecx, dest| { + ecx.write_immediate(*imm, dest) + }) + .ok()?; + + let literal = ConstantKind::Val( + ConstValue::ByRef { alloc, offset: Size::ZERO }, + value.layout.ty, + ); + Some(Operand::Constant(Box::new(Constant { + span: DUMMY_SP, + user_ty: None, + literal, + }))) } - _ => false, + // Scalars or scalar pairs that contain undef values are assumed to not have + // successfully evaluated and are thus not propagated. + _ => None, } } @@ -810,12 +719,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { self.super_operand(operand, location); - - // Only const prop copies and moves on `mir_opt_level=3` as doing so - // currently slightly increases compile time in some cases. - if self.tcx.sess.mir_opt_level() >= 3 { - self.propagate_operand(operand) - } + self.propagate_operand(operand) } fn process_projection_elem( @@ -825,8 +729,7 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { ) -> Option<PlaceElem<'tcx>> { if let PlaceElem::Index(local) = elem && let Some(value) = self.get_const(local.into()) - && self.should_const_prop(&value) - && let interpret::Operand::Immediate(interpret::Immediate::Scalar(scalar)) = *value + && let interpret::Operand::Immediate(Immediate::Scalar(scalar)) = *value && let Ok(offset) = scalar.to_target_usize(&self.tcx) && let Some(min_length) = offset.checked_add(1) { @@ -852,7 +755,14 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { ConstPropMode::NoPropagation => self.ensure_not_propagated(place.local), ConstPropMode::OnlyInsideOwnBlock | ConstPropMode::FullConstProp => { if let Some(()) = self.eval_rvalue_with_identities(rvalue, *place) { - self.replace_with_const(*place, rvalue); + // If this was already an evaluated constant, keep it. + if let Rvalue::Use(Operand::Constant(c)) = rvalue + && let ConstantKind::Val(..) = c.literal + { + trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c); + } else if let Some(operand) = self.replace_with_const(*place) { + *rvalue = Rvalue::Use(operand); + } } else { // Const prop failed, so erase the destination, ensuring that whatever happens // from here on, does not know about the previous value. @@ -919,45 +829,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { } } - fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) { - self.super_terminator(terminator, location); - - match &mut terminator.kind { - TerminatorKind::Assert { expected, ref mut cond, .. } => { - if let Some(ref value) = self.eval_operand(&cond) - && let Ok(value_const) = self.ecx.read_scalar(&value) - && self.should_const_prop(value) - { - trace!("assertion on {:?} should be {:?}", value, expected); - *cond = self.operand_from_scalar(value_const, self.tcx.types.bool); - } - } - TerminatorKind::SwitchInt { ref mut discr, .. } => { - // FIXME: This is currently redundant with `visit_operand`, but sadly - // always visiting operands currently causes a perf regression in LLVM codegen, so - // `visit_operand` currently only runs for propagates places for `mir_opt_level=4`. - self.propagate_operand(discr) - } - // None of these have Operands to const-propagate. - TerminatorKind::Goto { .. } - | TerminatorKind::Resume - | TerminatorKind::Terminate - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } - | TerminatorKind::Yield { .. } - | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::InlineAsm { .. } => {} - // Every argument in our function calls have already been propagated in `visit_operand`. - // - // NOTE: because LLVM codegen gives slight performance regressions with it, so this is - // gated on `mir_opt_level=3`. - TerminatorKind::Call { .. } => {} - } - } - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { self.super_basic_block_data(block, data); diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 01b945afda6..ac07c25763b 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_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 658e01d9310..97bdb878ab1 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -16,8 +16,8 @@ use rustc_middle::mir::coverage::*; /// `Coverage` statements. pub(super) struct CoverageCounters { function_source_hash: u64, - next_counter_id: u32, - num_expressions: u32, + next_counter_id: CounterId, + next_expression_id: ExpressionId, pub debug_counters: DebugCounters, } @@ -25,8 +25,8 @@ impl CoverageCounters { pub fn new(function_source_hash: u64) -> Self { Self { function_source_hash, - next_counter_id: CounterValueReference::START.as_u32(), - num_expressions: 0, + next_counter_id: CounterId::START, + next_expression_id: ExpressionId::START, debug_counters: DebugCounters::new(), } } @@ -65,9 +65,9 @@ impl CoverageCounters { fn make_expression<F>( &mut self, - lhs: ExpressionOperandId, + lhs: Operand, op: Op, - rhs: ExpressionOperandId, + rhs: Operand, debug_block_label_fn: F, ) -> CoverageKind where @@ -81,33 +81,30 @@ impl CoverageCounters { expression } - pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { + pub fn make_identity_counter(&mut self, counter_operand: Operand) -> CoverageKind { let some_debug_block_label = if self.debug_counters.is_enabled() { self.debug_counters.some_block_label(counter_operand).cloned() } else { None }; - self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + self.make_expression(counter_operand, Op::Add, Operand::Zero, || { some_debug_block_label.clone() }) } /// Counter IDs start from one and go up. - fn next_counter(&mut self) -> CounterValueReference { - assert!(self.next_counter_id < u32::MAX - self.num_expressions); + fn next_counter(&mut self) -> CounterId { let next = self.next_counter_id; - self.next_counter_id += 1; - CounterValueReference::from(next) + self.next_counter_id = next.next_id(); + next } - /// Expression IDs start from u32::MAX and go down because an Expression can reference - /// (add or subtract counts) of both Counter regions and Expression regions. The counter - /// expression operand IDs must be unique across both types. - fn next_expression(&mut self) -> InjectedExpressionId { - assert!(self.next_counter_id < u32::MAX - self.num_expressions); - let next = u32::MAX - self.num_expressions; - self.num_expressions += 1; - InjectedExpressionId::from(next) + /// Expression IDs start from 0 and go up. + /// (Counter IDs and Expression IDs are distinguished by the `Operand` enum.) + fn next_expression(&mut self) -> ExpressionId { + let next = self.next_expression_id; + self.next_expression_id = next.next_id(); + next } } @@ -199,7 +196,7 @@ impl<'a> BcbCounters<'a> { &mut self, traversal: &mut TraverseCoverageGraphWithLoops, branching_bcb: BasicCoverageBlock, - branching_counter_operand: ExpressionOperandId, + branching_counter_operand: Operand, collect_intermediate_expressions: &mut Vec<CoverageKind>, ) -> Result<(), Error> { let branches = self.bcb_branches(branching_bcb); @@ -261,7 +258,7 @@ impl<'a> BcbCounters<'a> { " [new intermediate expression: {}]", self.format_counter(&intermediate_expression) ); - let intermediate_expression_operand = intermediate_expression.as_operand_id(); + let intermediate_expression_operand = intermediate_expression.as_operand(); collect_intermediate_expressions.push(intermediate_expression); some_sumup_counter_operand.replace(intermediate_expression_operand); } @@ -282,7 +279,7 @@ impl<'a> BcbCounters<'a> { branching_counter_operand, Op::Subtract, sumup_counter_operand, - || Some(format!("{:?}", expression_branch)), + || Some(format!("{expression_branch:?}")), ); debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); let bcb = expression_branch.target_bcb; @@ -298,7 +295,7 @@ impl<'a> BcbCounters<'a> { &mut self, bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec<CoverageKind>, - ) -> Result<ExpressionOperandId, Error> { + ) -> Result<Operand, Error> { self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1) } @@ -307,7 +304,7 @@ impl<'a> BcbCounters<'a> { bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec<CoverageKind>, debug_indent_level: usize, - ) -> Result<ExpressionOperandId, Error> { + ) -> Result<Operand, Error> { // If the BCB already has a counter, return it. if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { debug!( @@ -316,7 +313,7 @@ impl<'a> BcbCounters<'a> { bcb, self.format_counter(counter_kind), ); - return Ok(counter_kind.as_operand_id()); + return Ok(counter_kind.as_operand()); } // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`). @@ -324,7 +321,7 @@ impl<'a> BcbCounters<'a> { // program results in a tight infinite loop, but it should still compile. let one_path_to_target = self.bcb_has_one_path_to_target(bcb); if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { - let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{:?}", bcb))); + let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{bcb:?}"))); if one_path_to_target { debug!( "{}{:?} gets a new counter: {}", @@ -383,7 +380,7 @@ impl<'a> BcbCounters<'a> { NESTED_INDENT.repeat(debug_indent_level), self.format_counter(&intermediate_expression) ); - let intermediate_expression_operand = intermediate_expression.as_operand_id(); + let intermediate_expression_operand = intermediate_expression.as_operand(); collect_intermediate_expressions.push(intermediate_expression); some_sumup_edge_counter_operand.replace(intermediate_expression_operand); } @@ -392,7 +389,7 @@ impl<'a> BcbCounters<'a> { first_edge_counter_operand, Op::Add, some_sumup_edge_counter_operand.unwrap(), - || Some(format!("{:?}", bcb)), + || Some(format!("{bcb:?}")), ); debug!( "{}{:?} gets a new counter (sum of predecessor counters): {}", @@ -408,7 +405,7 @@ impl<'a> BcbCounters<'a> { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec<CoverageKind>, - ) -> Result<ExpressionOperandId, Error> { + ) -> Result<Operand, Error> { self.recursive_get_or_make_edge_counter_operand( from_bcb, to_bcb, @@ -423,7 +420,7 @@ impl<'a> BcbCounters<'a> { to_bcb: BasicCoverageBlock, collect_intermediate_expressions: &mut Vec<CoverageKind>, debug_indent_level: usize, - ) -> Result<ExpressionOperandId, Error> { + ) -> Result<Operand, Error> { // If the source BCB has only one successor (assumed to be the given target), an edge // counter is unnecessary. Just get or make a counter for the source BCB. let successors = self.bcb_successors(from_bcb).iter(); @@ -444,12 +441,12 @@ impl<'a> BcbCounters<'a> { to_bcb, self.format_counter(counter_kind) ); - return Ok(counter_kind.as_operand_id()); + return Ok(counter_kind.as_operand()); } // Make a new counter to count this edge. let counter_kind = - self.coverage_counters.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + self.coverage_counters.make_counter(|| Some(format!("{from_bcb:?}->{to_bcb:?}"))); debug!( "{}Edge {:?}->{:?} gets a new counter: {}", NESTED_INDENT.repeat(debug_indent_level), diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index 7ad98144159..26f9cfd0b86 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -246,7 +246,7 @@ impl Default for ExpressionFormat { } } -/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `ExpressionOperandId`) to +/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `Operand`) to /// the `CoverageKind` data and optional label (normally, the counter's associated /// `BasicCoverageBlock` format string, if any). /// @@ -258,7 +258,7 @@ impl Default for ExpressionFormat { /// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be /// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. pub(super) struct DebugCounters { - some_counters: Option<FxHashMap<ExpressionOperandId, DebugCounter>>, + some_counters: Option<FxHashMap<Operand, DebugCounter>>, } impl DebugCounters { @@ -277,14 +277,14 @@ impl DebugCounters { pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option<String>) { if let Some(counters) = &mut self.some_counters { - let id = counter_kind.as_operand_id(); + let id = counter_kind.as_operand(); counters .try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label)) .expect("attempt to add the same counter_kind to DebugCounters more than once"); } } - pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { + pub fn some_block_label(&self, operand: Operand) -> Option<&String> { self.some_counters.as_ref().and_then(|counters| { counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref()) }) @@ -323,24 +323,24 @@ impl DebugCounters { } } - let id = counter_kind.as_operand_id(); + let id = counter_kind.as_operand(); if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { let counters = self.some_counters.as_ref().unwrap(); if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = counters.get(&id) { return if counter_format.id { - format!("{}#{}", block_label, id.index()) + format!("{}#{:?}", block_label, id) } else { block_label.to_string() }; } } - format!("#{}", id.index()) + format!("#{:?}", id) } - fn format_operand(&self, operand: ExpressionOperandId) -> String { - if operand.index() == 0 { + fn format_operand(&self, operand: Operand) -> String { + if matches!(operand, Operand::Zero) { return String::from("0"); } if let Some(counters) = &self.some_counters { @@ -358,7 +358,7 @@ impl DebugCounters { return self.format_counter_kind(counter_kind); } } - format!("#{}", operand.index()) + format!("#{:?}", operand) } } @@ -485,8 +485,7 @@ impl GraphvizData { /// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs /// and/or a `CoverageGraph` graphviz output). pub(super) struct UsedExpressions { - some_used_expression_operands: - Option<FxHashMap<ExpressionOperandId, Vec<InjectedExpressionId>>>, + some_used_expression_operands: Option<FxHashMap<Operand, Vec<ExpressionId>>>, some_unused_expressions: Option<Vec<(CoverageKind, Option<BasicCoverageBlock>, BasicCoverageBlock)>>, } @@ -517,7 +516,7 @@ impl UsedExpressions { pub fn expression_is_used(&self, expression: &CoverageKind) -> bool { if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { - used_expression_operands.contains_key(&expression.as_operand_id()) + used_expression_operands.contains_key(&expression.as_operand()) } else { false } @@ -530,7 +529,7 @@ impl UsedExpressions { target_bcb: BasicCoverageBlock, ) { if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { - if !used_expression_operands.contains_key(&expression.as_operand_id()) { + if !used_expression_operands.contains_key(&expression.as_operand()) { self.some_unused_expressions.as_mut().unwrap().push(( expression.clone(), edge_from_bcb, @@ -630,7 +629,7 @@ pub(super) fn dump_coverage_spanview<'tcx>( .expect("Unexpected error creating MIR spanview HTML file"); let crate_name = tcx.crate_name(def_id.krate); let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); - let title = format!("{}.{} - Coverage Spans", crate_name, item_name); + let title = format!("{crate_name}.{item_name} - Coverage Spans"); spanview::write_document(tcx, body_span, span_viewables, &title, &mut file) .expect("Unexpected IO error dumping coverage spans as HTML"); } @@ -779,7 +778,7 @@ fn bcb_to_string_sections<'tcx>( )); } if let Some(counter_kind) = &bcb_data.counter_kind { - sections.push(format!("{:?}", counter_kind)); + sections.push(format!("{counter_kind:?}")); } let non_term_blocks = bcb_data.basic_blocks[0..len - 1] .iter() diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index d2a854b2675..f94dad4c8da 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -345,10 +345,7 @@ impl BasicCoverageBlockData { &mir_body[self.last_bb()].terminator() } - pub fn set_counter( - &mut self, - counter_kind: CoverageKind, - ) -> Result<ExpressionOperandId, Error> { + pub fn set_counter(&mut self, counter_kind: CoverageKind) -> Result<Operand, Error> { debug_assert!( // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also // have an expression (to be injected into an existing `BasicBlock` represented by this @@ -356,12 +353,11 @@ impl BasicCoverageBlockData { self.edge_from_bcbs.is_none() || counter_kind.is_expression(), "attempt to add a `Counter` to a BCB target with existing incoming edge counters" ); - let operand = counter_kind.as_operand_id(); + let operand = counter_kind.as_operand(); if let Some(replaced) = self.counter_kind.replace(counter_kind) { Error::from_string(format!( "attempt to set a BasicCoverageBlock coverage counter more than once; \ - {:?} already had counter {:?}", - self, replaced, + {self:?} already had counter {replaced:?}", )) } else { Ok(operand) @@ -382,27 +378,25 @@ impl BasicCoverageBlockData { &mut self, from_bcb: BasicCoverageBlock, counter_kind: CoverageKind, - ) -> Result<ExpressionOperandId, Error> { + ) -> Result<Operand, Error> { if level_enabled!(tracing::Level::DEBUG) { // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also // have an expression (to be injected into an existing `BasicBlock` represented by this // `BasicCoverageBlock`). if self.counter_kind.as_ref().is_some_and(|c| !c.is_expression()) { return Error::from_string(format!( - "attempt to add an incoming edge counter from {:?} when the target BCB already \ - has a `Counter`", - from_bcb + "attempt to add an incoming edge counter from {from_bcb:?} when the target BCB already \ + has a `Counter`" )); } } - let operand = counter_kind.as_operand_id(); + let operand = counter_kind.as_operand(); if let Some(replaced) = self.edge_from_bcbs.get_or_insert_default().insert(from_bcb, counter_kind) { Error::from_string(format!( "attempt to set an edge counter more than once; from_bcb: \ - {:?} already had counter {:?}", - from_bcb, replaced, + {from_bcb:?} already had counter {replaced:?}", )) } else { Ok(operand) @@ -612,7 +606,7 @@ impl TraverseCoverageGraphWithLoops { the {}", successor_to_add, if let Some(loop_header) = some_loop_header { - format!("worklist for the loop headed by {:?}", loop_header) + format!("worklist for the loop headed by {loop_header:?}") } else { String::from("non-loop worklist") }, @@ -623,7 +617,7 @@ impl TraverseCoverageGraphWithLoops { "{:?} successor is non-branching. Defer it to the end of the {}", successor_to_add, if let Some(loop_header) = some_loop_header { - format!("worklist for the loop headed by {:?}", loop_header) + format!("worklist for the loop headed by {loop_header:?}") } else { String::from("non-loop worklist") }, diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 076e714d703..f713613d313 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -304,7 +304,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> { let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { self.coverage_counters.make_identity_counter(counter_operand) } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { - bcb_counters[bcb] = Some(counter_kind.as_operand_id()); + bcb_counters[bcb] = Some(counter_kind.as_operand()); debug_used_expressions.add_expression_operands(&counter_kind); counter_kind } else { diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 74b4b4a07c5..aa205655f9d 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -43,43 +43,25 @@ struct CoverageVisitor { } impl CoverageVisitor { - /// Updates `num_counters` to the maximum encountered zero-based counter_id plus 1. Note the - /// final computed number of counters should be the number of all `CoverageKind::Counter` - /// statements in the MIR *plus one* for the implicit `ZERO` counter. + /// Updates `num_counters` to the maximum encountered counter ID plus 1. #[inline(always)] - fn update_num_counters(&mut self, counter_id: u32) { + fn update_num_counters(&mut self, counter_id: CounterId) { + let counter_id = counter_id.as_u32(); self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); } - /// Computes an expression index for each expression ID, and updates `num_expressions` to the - /// maximum encountered index plus 1. + /// Updates `num_expressions` to the maximum encountered expression ID plus 1. #[inline(always)] - fn update_num_expressions(&mut self, expression_id: u32) { - let expression_index = u32::MAX - expression_id; - self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1); + fn update_num_expressions(&mut self, expression_id: ExpressionId) { + let expression_id = expression_id.as_u32(); + self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_id + 1); } - fn update_from_expression_operand(&mut self, operand_id: u32) { - if operand_id >= self.info.num_counters { - let operand_as_expression_index = u32::MAX - operand_id; - if operand_as_expression_index >= self.info.num_expressions { - // The operand ID is outside the known range of counter IDs and also outside the - // known range of expression IDs. In either case, the result of a missing operand - // (if and when used in an expression) will be zero, so from a computation - // perspective, it doesn't matter whether it is interpreted as a counter or an - // expression. - // - // However, the `num_counters` and `num_expressions` query results are used to - // allocate arrays when generating the coverage map (during codegen), so choose - // the type that grows either `num_counters` or `num_expressions` the least. - if operand_id - self.info.num_counters - < operand_as_expression_index - self.info.num_expressions - { - self.update_num_counters(operand_id) - } else { - self.update_num_expressions(operand_id) - } - } + fn update_from_expression_operand(&mut self, operand: Operand) { + match operand { + Operand::Counter(id) => self.update_num_counters(id), + Operand::Expression(id) => self.update_num_expressions(id), + Operand::Zero => {} } } @@ -100,19 +82,15 @@ impl CoverageVisitor { if self.add_missing_operands { match coverage.kind { CoverageKind::Expression { lhs, rhs, .. } => { - self.update_from_expression_operand(u32::from(lhs)); - self.update_from_expression_operand(u32::from(rhs)); + self.update_from_expression_operand(lhs); + self.update_from_expression_operand(rhs); } _ => {} } } else { match coverage.kind { - CoverageKind::Counter { id, .. } => { - self.update_num_counters(u32::from(id)); - } - CoverageKind::Expression { id, .. } => { - self.update_num_expressions(u32::from(id)); - } + CoverageKind::Counter { id, .. } => self.update_num_counters(id), + CoverageKind::Expression { id, .. } => self.update_num_expressions(id), _ => {} } } @@ -123,8 +101,7 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> let mir_body = tcx.instance_mir(instance_def); let mut coverage_visitor = CoverageVisitor { - // num_counters always has at least the `ZERO` counter. - info: CoverageInfo { num_counters: 1, num_expressions: 0 }, + info: CoverageInfo { num_counters: 0, num_expressions: 0 }, add_missing_operands: false, }; diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 35cf9ea5f91..deebf5345ba 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::source_map::original_sp; use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol}; -use std::cell::RefCell; +use std::cell::OnceCell; use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] @@ -67,7 +67,7 @@ impl CoverageStatement { pub(super) struct CoverageSpan { pub span: Span, pub expn_span: Span, - pub current_macro_or_none: RefCell<Option<Option<Symbol>>>, + pub current_macro_or_none: OnceCell<Option<Symbol>>, pub bcb: BasicCoverageBlock, pub coverage_statements: Vec<CoverageStatement>, pub is_closure: bool, @@ -175,8 +175,7 @@ impl CoverageSpan { /// If the span is part of a macro, returns the macro name symbol. pub fn current_macro(&self) -> Option<Symbol> { self.current_macro_or_none - .borrow_mut() - .get_or_insert_with(|| { + .get_or_init(|| { if let ExpnKind::Macro(MacroKind::Bang, current_macro) = self.expn_span.ctxt().outer_expn_data().kind { diff --git a/compiler/rustc_mir_transform/src/coverage/test_macros/src/lib.rs b/compiler/rustc_mir_transform/src/coverage/test_macros/src/lib.rs index 3d6095d2738..f41adf667ec 100644 --- a/compiler/rustc_mir_transform/src/coverage/test_macros/src/lib.rs +++ b/compiler/rustc_mir_transform/src/coverage/test_macros/src/lib.rs @@ -2,5 +2,5 @@ use proc_macro::TokenStream; #[proc_macro] pub fn let_bcb(item: TokenStream) -> TokenStream { - format!("let bcb{} = graph::BasicCoverageBlock::from_usize({});", item, item).parse().unwrap() + format!("let bcb{item} = graph::BasicCoverageBlock::from_usize({item});").parse().unwrap() } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 25891d3ca0f..248a192f8f5 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -683,7 +683,7 @@ fn test_make_bcb_counters() { let_bcb!(1); assert_eq!( - 1, // coincidentally, bcb1 has a `Counter` with id = 1 + 0, // bcb1 has a `Counter` with id = 0 match basic_coverage_blocks[bcb1].counter().expect("should have a counter") { CoverageKind::Counter { id, .. } => id, _ => panic!("expected a Counter"), @@ -693,7 +693,7 @@ fn test_make_bcb_counters() { let_bcb!(2); assert_eq!( - 2, // coincidentally, bcb2 has a `Counter` with id = 2 + 1, // bcb2 has a `Counter` with id = 1 match basic_coverage_blocks[bcb2].counter().expect("should have a counter") { CoverageKind::Counter { id, .. } => id, _ => panic!("expected a Counter"), diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 7bc5183a00a..3f988930b5e 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -13,9 +13,12 @@ //! use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_mir_dataflow::impls::{borrowed_locals, MaybeTransitiveLiveLocals}; +use rustc_mir_dataflow::impls::{ + borrowed_locals, LivenessTransferFunction, MaybeTransitiveLiveLocals, +}; use rustc_mir_dataflow::Analysis; /// Performs the optimization on the body @@ -28,8 +31,33 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS .iterate_to_fixpoint() .into_results_cursor(body); + // For blocks with a call terminator, if an argument copy can be turned into a move, + // record it as (block, argument index). + let mut call_operands_to_move = Vec::new(); let mut patch = Vec::new(); + for (bb, bb_data) in traversal::preorder(body) { + if let TerminatorKind::Call { ref args, .. } = bb_data.terminator().kind { + let loc = Location { block: bb, statement_index: bb_data.statements.len() }; + + // Position ourselves between the evaluation of `args` and the write to `destination`. + live.seek_to_block_end(bb); + let mut state = live.get().clone(); + + for (index, arg) in args.iter().enumerate().rev() { + if let Operand::Copy(place) = *arg + && !place.is_indirect() + && !borrowed.contains(place.local) + && !state.contains(place.local) + { + call_operands_to_move.push((bb, index)); + } + + // Account that `arg` is read from, so we don't promote another argument to a move. + LivenessTransferFunction(&mut state).visit_operand(arg, loc); + } + } + for (statement_index, statement) in bb_data.statements.iter().enumerate().rev() { let loc = Location { block: bb, statement_index }; if let StatementKind::Assign(assign) = &statement.kind { @@ -64,7 +92,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS } } - if patch.is_empty() { + if patch.is_empty() && call_operands_to_move.is_empty() { return; } @@ -72,6 +100,14 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS for Location { block, statement_index } in patch { bbs[block].statements[statement_index].make_nop(); } + for (block, argument_index) in call_operands_to_move { + let TerminatorKind::Call { ref mut args, .. } = bbs[block].terminator_mut().kind else { + bug!() + }; + let arg = &mut args[argument_index]; + let Operand::Copy(place) = *arg else { bug!() }; + *arg = Operand::Move(place); + } crate::simplify::simplify_locals(body, tcx) } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index d5664e2b40a..43757a9ea35 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -358,8 +358,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.tcx.sess.delay_span_bug( terminator.source_info.span, format!( - "drop of untracked, uninitialized value {:?}, place {:?} ({:?})", - bb, place, path + "drop of untracked, uninitialized value {bb:?}, place {place:?} ({path:?})" ), ); } @@ -424,7 +423,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { if !replace { self.tcx.sess.delay_span_bug( terminator.source_info.span, - format!("drop of untracked value {:?}", bb), + format!("drop of untracked value {bb:?}"), ); } // A drop and replace behind a pointer/array/whatever. diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index fdf6ab33c82..a42eacbf22b 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -168,15 +168,15 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { } }; let ident = self.tcx.item_name(fn_id).to_ident_string(); - let ty_params = fn_args.types().map(|ty| format!("{}", ty)); - let const_params = fn_args.consts().map(|c| format!("{}", c)); + let ty_params = fn_args.types().map(|ty| format!("{ty}")); + let const_params = fn_args.consts().map(|c| format!("{c}")); let params = ty_params.chain(const_params).join(", "); let num_args = fn_sig.inputs().map_bound(|inputs| inputs.len()).skip_binder(); let variadic = if fn_sig.c_variadic() { ", ..." } else { "" }; let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; let sugg = format!( "{} as {}{}fn({}{}){}", - if params.is_empty() { ident.clone() } else { format!("{}::<{}>", ident, params) }, + if params.is_empty() { ident.clone() } else { format!("{ident}::<{params}>") }, unsafety, abi, vec!["_"; num_args].join(", "), diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index eaeaff69cf3..669135f80bc 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1477,7 +1477,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform { ) } _ => { - tcx.sess.delay_span_bug(body.span, format!("unexpected generator type {}", gen_ty)); + tcx.sess.delay_span_bug(body.span, format!("unexpected generator type {gen_ty}")); return; } }; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 7860cf76247..f13c8214af1 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -1,6 +1,7 @@ //! Inlining pass for MIR functions use crate::deref_separator::deref_finder; use rustc_attr::InlineAttr; +use rustc_const_eval::transform::validate::validate_types; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; use rustc_index::Idx; @@ -10,7 +11,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_session::config::OptLevel; -use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; +use rustc_target::abi::FieldIdx; use rustc_target::spec::abi::Abi; use crate::simplify::{remove_dead_blocks, CfgSimplifier}; @@ -200,6 +201,19 @@ impl<'tcx> Inliner<'tcx> { return Err("failed to normalize callee body"); }; + // Normally, this shouldn't be required, but trait normalization failure can create a + // validation ICE. + if !validate_types( + self.tcx, + MirPhase::Runtime(RuntimePhase::Optimized), + self.param_env, + &callee_body, + ) + .is_empty() + { + return Err("failed to validate callee body"); + } + // Check call signature compatibility. // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. @@ -437,7 +451,6 @@ impl<'tcx> Inliner<'tcx> { instance: callsite.callee, callee_body, cost: 0, - validation: Ok(()), }; // Traverse the MIR manually so we can account for the effects of inlining on the CFG. @@ -476,9 +489,6 @@ impl<'tcx> Inliner<'tcx> { } } - // Abort if type validation found anything fishy. - checker.validation?; - // N.B. We still apply our cost threshold to #[inline(always)] functions. // That attribute is often applied to very large functions that exceed LLVM's (very // generous) inlining threshold. Such functions are very poor MIR inlining candidates. @@ -770,11 +780,10 @@ struct CostChecker<'b, 'tcx> { cost: usize, callee_body: &'b Body<'tcx>, instance: ty::Instance<'tcx>, - validation: Result<(), &'static str>, } impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { // Don't count StorageLive/StorageDead in the inlining cost. match statement.kind { StatementKind::StorageLive(_) @@ -783,11 +792,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { | StatementKind::Nop => {} _ => self.cost += INSTR_COST, } - - self.super_statement(statement, location); } - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) { let tcx = self.tcx; match terminator.kind { TerminatorKind::Drop { ref place, unwind, .. } => { @@ -831,100 +838,6 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } _ => self.cost += INSTR_COST, } - - self.super_terminator(terminator, location); - } - - /// This method duplicates code from MIR validation in an attempt to detect type mismatches due - /// to normalization failure. - fn visit_projection_elem( - &mut self, - place_ref: PlaceRef<'tcx>, - elem: PlaceElem<'tcx>, - context: PlaceContext, - location: Location, - ) { - if let ProjectionElem::Field(f, ty) = elem { - let parent_ty = place_ref.ty(&self.callee_body.local_decls, self.tcx); - let check_equal = |this: &mut Self, f_ty| { - if !util::is_equal_up_to_subtyping(this.tcx, this.param_env, ty, f_ty) { - trace!(?ty, ?f_ty); - this.validation = Err("failed to normalize projection type"); - return; - } - }; - - let kind = match parent_ty.ty.kind() { - &ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { - self.tcx.type_of(def_id).instantiate(self.tcx, args).kind() - } - kind => kind, - }; - - match kind { - ty::Tuple(fields) => { - let Some(f_ty) = fields.get(f.as_usize()) else { - self.validation = Err("malformed MIR"); - return; - }; - check_equal(self, *f_ty); - } - ty::Adt(adt_def, args) => { - let var = parent_ty.variant_index.unwrap_or(FIRST_VARIANT); - let Some(field) = adt_def.variant(var).fields.get(f) else { - self.validation = Err("malformed MIR"); - return; - }; - check_equal(self, field.ty(self.tcx, args)); - } - ty::Closure(_, args) => { - let args = args.as_closure(); - let Some(f_ty) = args.upvar_tys().nth(f.as_usize()) else { - self.validation = Err("malformed MIR"); - return; - }; - check_equal(self, f_ty); - } - &ty::Generator(def_id, args, _) => { - let f_ty = if let Some(var) = parent_ty.variant_index { - let gen_body = if def_id == self.callee_body.source.def_id() { - self.callee_body - } else { - self.tcx.optimized_mir(def_id) - }; - - let Some(layout) = gen_body.generator_layout() else { - self.validation = Err("malformed MIR"); - return; - }; - - let Some(&local) = layout.variant_fields[var].get(f) else { - self.validation = Err("malformed MIR"); - return; - }; - - let Some(f_ty) = layout.field_tys.get(local) else { - self.validation = Err("malformed MIR"); - return; - }; - - f_ty.ty - } else { - let Some(f_ty) = args.as_generator().prefix_tys().nth(f.index()) else { - self.validation = Err("malformed MIR"); - return; - }; - - f_ty - }; - - check_equal(self, f_ty); - } - _ => self.validation = Err("malformed MIR"), - } - } - - self.super_projection_elem(place_ref, elem, context, location); } } diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index a430f8da35c..8b0a0903d18 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -57,7 +57,7 @@ struct InstSimplifyContext<'tcx, 'a> { impl<'tcx> InstSimplifyContext<'tcx, '_> { fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { self.tcx.consider_optimizing(|| { - format!("InstSimplify - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info) + format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}") }) } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index d419329f2d6..734321e97d8 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -615,7 +615,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> { // computes and caches its result. Some(hir::ConstContext::ConstFn) => tcx.ensure_with_value().mir_for_ctfe(did), None => {} - Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other), + Some(other) => panic!("do not use `optimized_mir` for constants: {other:?}"), } debug!("about to call mir_drops_elaborated..."); let body = tcx.mir_drops_elaborated_and_const_checked(did).steal(); diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 6eb48498274..bc29fb8ded1 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { let bbs = body.basic_blocks.as_mut(); let mut should_cleanup = false; 'outer: for bb_idx in bbs.indices() { - if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) { + if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {def_id:?} ")) { continue; } diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index 3957cd92c4e..c97d034544a 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -27,7 +27,7 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators { } for bb in bbs { - if !tcx.consider_optimizing(|| format!("MultipleReturnTerminators {:?} ", def_id)) { + if !tcx.consider_optimizing(|| format!("MultipleReturnTerminators {def_id:?} ")) { break; } diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index 5ce96012b90..e1298b0654f 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -45,7 +45,7 @@ impl<'tcx> MirPass<'tcx> for RenameReturnPlace { return; }; - if !tcx.consider_optimizing(|| format!("RenameReturnPlace {:?}", def_id)) { + if !tcx.consider_optimizing(|| format!("RenameReturnPlace {def_id:?}")) { return; } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 710eed3ed38..057f5fe8293 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -118,7 +118,7 @@ fn run_passes_inner<'tcx>( dump_mir_for_pass(tcx, body, &name, false); } if validate { - validate_body(tcx, body, format!("before pass {}", name)); + validate_body(tcx, body, format!("before pass {name}")); } tcx.sess.time(name, || pass.run_pass(tcx, body)); @@ -127,7 +127,7 @@ fn run_passes_inner<'tcx>( dump_mir_for_pass(tcx, body, &name, true); } if validate { - validate_body(tcx, body, format!("after pass {}", name)); + validate_body(tcx, body, format!("after pass {name}")); } body.pass_count += 1; diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 84ccf6e1f61..08b2a6537e9 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -27,7 +27,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { if ty.ty.needs_drop(tcx, param_env) { continue; } - if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {:?} ", did)) { + if !tcx.consider_optimizing(|| format!("RemoveUnneededDrops {did:?} ")) { continue; } debug!("SUCCESS: replacing `drop` with goto({:?})", target); diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 1ba9ad87537..9c6c55b0811 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -102,7 +102,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> { let op_ty = operand.ty(self.local_decls, self.tcx); if self.known_to_be_zst(op_ty) && self.tcx.consider_optimizing(|| { - format!("RemoveZsts - Operand: {:?} Location: {:?}", operand, loc) + format!("RemoveZsts - Operand: {operand:?} Location: {loc:?}") }) { *operand = Operand::Constant(Box::new(self.make_zst(op_ty))) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 54096abb2e0..de6db8ae6ae 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -248,7 +248,8 @@ where } let size_estimate = mono_item.size_estimate(cx.tcx); - cgu.items_mut().insert(mono_item, MonoItemData { linkage, visibility, size_estimate }); + cgu.items_mut() + .insert(mono_item, MonoItemData { inlined: false, linkage, visibility, size_estimate }); // Get all inlined items that are reachable from `mono_item` without // going via another root item. This includes drop-glue, functions from @@ -263,6 +264,7 @@ where for inlined_item in reachable_inlined_items { // This is a CGU-private copy. cgu.items_mut().entry(inlined_item).or_insert_with(|| MonoItemData { + inlined: true, linkage: Linkage::Internal, visibility: Visibility::Default, size_estimate: inlined_item.size_estimate(cx.tcx), @@ -316,6 +318,60 @@ fn merge_codegen_units<'tcx>( let mut cgu_contents: FxHashMap<Symbol, Vec<Symbol>> = codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect(); + // If N is the maximum number of CGUs, and the CGUs are sorted from largest + // to smallest, we repeatedly find which CGU in codegen_units[N..] has the + // greatest overlap of inlined items with codegen_units[N-1], merge that + // CGU into codegen_units[N-1], then re-sort by size and repeat. + // + // We use inlined item overlap to guide this merging because it minimizes + // duplication of inlined items, which makes LLVM be faster and generate + // better and smaller machine code. + // + // Why merge into codegen_units[N-1]? We want CGUs to have similar sizes, + // which means we don't want codegen_units[0..N] (the already big ones) + // getting any bigger, if we can avoid it. When we have more than N CGUs + // then at least one of the biggest N will have to grow. codegen_units[N-1] + // is the smallest of those, and so has the most room to grow. + let max_codegen_units = cx.tcx.sess.codegen_units().as_usize(); + while codegen_units.len() > max_codegen_units { + // Sort small CGUs to the back. + codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate())); + + let cgu_dst = &codegen_units[max_codegen_units - 1]; + + // Find the CGU that overlaps the most with `cgu_dst`. In the case of a + // tie, favour the earlier (bigger) CGU. + let mut max_overlap = 0; + let mut max_overlap_i = max_codegen_units; + for (i, cgu_src) in codegen_units.iter().enumerate().skip(max_codegen_units) { + if cgu_src.size_estimate() <= max_overlap { + // None of the remaining overlaps can exceed `max_overlap`, so + // stop looking. + break; + } + + let overlap = compute_inlined_overlap(cgu_dst, cgu_src); + if overlap > max_overlap { + max_overlap = overlap; + max_overlap_i = i; + } + } + + let mut cgu_src = codegen_units.swap_remove(max_overlap_i); + let cgu_dst = &mut codegen_units[max_codegen_units - 1]; + + // Move the items from `cgu_src` to `cgu_dst`. Some of them may be + // duplicate inlined items, in which case the destination CGU is + // unaffected. Recalculate size estimates afterwards. + cgu_dst.items_mut().extend(cgu_src.items_mut().drain()); + cgu_dst.compute_size_estimate(); + + // Record that `cgu_dst` now contains all the stuff that was in + // `cgu_src` before. + let mut consumed_cgu_names = cgu_contents.remove(&cgu_src.name()).unwrap(); + cgu_contents.get_mut(&cgu_dst.name()).unwrap().append(&mut consumed_cgu_names); + } + // Having multiple CGUs can drastically speed up compilation. But for // non-incremental builds, tiny CGUs slow down compilation *and* result in // worse generated code. So we don't allow CGUs smaller than this (unless @@ -323,21 +379,19 @@ fn merge_codegen_units<'tcx>( // common in larger programs, so this isn't all that large. const NON_INCR_MIN_CGU_SIZE: usize = 1800; - // Repeatedly merge the two smallest codegen units as long as: - // - we have more CGUs than the upper limit, or - // - (Non-incremental builds only) the user didn't specify a CGU count, and - // there are multiple CGUs, and some are below the minimum size. + // Repeatedly merge the two smallest codegen units as long as: it's a + // non-incremental build, and the user didn't specify a CGU count, and + // there are multiple CGUs, and some are below the minimum size. // // The "didn't specify a CGU count" condition is because when an explicit // count is requested we observe it as closely as possible. For example, // the `compiler_builtins` crate sets `codegen-units = 10000` and it's // critical they aren't merged. Also, some tests use explicit small values // and likewise won't work if small CGUs are merged. - while codegen_units.len() > cx.tcx.sess.codegen_units().as_usize() - || (cx.tcx.sess.opts.incremental.is_none() - && matches!(cx.tcx.sess.codegen_units(), CodegenUnits::Default(_)) - && codegen_units.len() > 1 - && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE)) + while cx.tcx.sess.opts.incremental.is_none() + && matches!(cx.tcx.sess.codegen_units(), CodegenUnits::Default(_)) + && codegen_units.len() > 1 + && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE) { // Sort small cgus to the back. codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate())); @@ -351,16 +405,7 @@ fn merge_codegen_units<'tcx>( second_smallest.items_mut().extend(smallest.items_mut().drain()); second_smallest.compute_size_estimate(); - // Record that `second_smallest` now contains all the stuff that was - // in `smallest` before. - let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap(); - cgu_contents.get_mut(&second_smallest.name()).unwrap().append(&mut consumed_cgu_names); - - debug!( - "CodegenUnit {} merged into CodegenUnit {}", - smallest.name(), - second_smallest.name() - ); + // Don't update `cgu_contents`, that's only for incremental builds. } let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); @@ -439,6 +484,25 @@ fn merge_codegen_units<'tcx>( } } +/// Compute the combined size of all inlined items that appear in both `cgu1` +/// and `cgu2`. +fn compute_inlined_overlap<'tcx>(cgu1: &CodegenUnit<'tcx>, cgu2: &CodegenUnit<'tcx>) -> usize { + // Either order works. We pick the one that involves iterating over fewer + // items. + let (src_cgu, dst_cgu) = + if cgu1.items().len() <= cgu2.items().len() { (cgu1, cgu2) } else { (cgu2, cgu1) }; + + let mut overlap = 0; + for (item, data) in src_cgu.items().iter() { + if data.inlined { + if dst_cgu.items().contains_key(item) { + overlap += data.size_estimate; + } + } + } + overlap +} + fn internalize_symbols<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], @@ -870,19 +934,16 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< all_cgu_sizes.push(cgu.size_estimate()); for (item, data) in cgu.items() { - match item.instantiation_mode(tcx) { - InstantiationMode::GloballyShared { .. } => { - root_items += 1; - root_size += data.size_estimate; - } - InstantiationMode::LocalCopy => { - if inlined_items.insert(item) { - unique_inlined_items += 1; - unique_inlined_size += data.size_estimate; - } - placed_inlined_items += 1; - placed_inlined_size += data.size_estimate; + if !data.inlined { + root_items += 1; + root_size += data.size_estimate; + } else { + if inlined_items.insert(item) { + unique_inlined_items += 1; + unique_inlined_size += data.size_estimate; } + placed_inlined_items += 1; + placed_inlined_size += data.size_estimate; } } } @@ -937,10 +998,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< let symbol_name = item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]); - let kind = match item.instantiation_mode(tcx) { - InstantiationMode::GloballyShared { .. } => "root", - InstantiationMode::LocalCopy => "inlined", - }; + let kind = if !data.inlined { "root" } else { "inlined" }; let size = data.size_estimate; let _ = with_no_trimmed_paths!(writeln!( s, @@ -983,10 +1041,7 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< } elem(curr, curr_count); - let mut s = "[".to_string(); - s.push_str(&v.join(", ")); - s.push_str("]"); - s + format!("[{}]", v.join(", ")) } }; @@ -1171,12 +1226,13 @@ fn dump_mono_items_stats<'tcx>( // Gather instantiated mono items grouped by def_id let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { - for (&mono_item, _) in cgu.items() { + cgu.items() + .keys() // Avoid variable-sized compiler-generated shims - if mono_item.is_user_defined() { + .filter(|mono_item| mono_item.is_user_defined()) + .for_each(|mono_item| { items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item); - } - } + }); } #[derive(serde::Serialize)] @@ -1229,7 +1285,7 @@ fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet { let mut result = items.clone(); for cgu in cgus { - for (item, _) in cgu.items() { + for item in cgu.items().keys() { if let MonoItem::Fn(ref instance) = item { let did = instance.def_id(); if !visited.insert(did) { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9787d98c1a4..83d96ad8e76 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -690,6 +690,8 @@ parse_single_colon_import_path = expected `::`, found `:` parse_single_colon_struct_type = found single colon in a struct field type path .suggestion = write a path separator here +parse_static_with_generics = static items may not have generic parameters + parse_struct_literal_body_without_path = struct literal body without path .suggestion = you might have forgotten to add the struct literal inside the block @@ -722,6 +724,10 @@ parse_sugg_wrap_pattern_in_parens = wrap the pattern in parentheses parse_switch_mut_let_order = switch the order of `mut` and `let` + +parse_ternary_operator = Rust has no ternary operator + .help = use an `if-else` expression instead + parse_tilde_const_lifetime = `~const` may only modify trait bounds, not lifetime bounds parse_tilde_is_not_unary_operator = `~` cannot be used as a unary operator @@ -847,6 +853,12 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a .label = the visibility .help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}` +parse_where_clause_before_const_body = where clauses are not allowed before const item bodies + .label = unexpected where clause + .name_label = while parsing this const item + .body_label = the item body + .suggestion = move the body before the where clause + parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies .label = unexpected where clause .name_label = while parsing this tuple struct diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 96e1c0e3c6d..06c09960727 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -365,6 +365,14 @@ pub(crate) enum IfExpressionMissingThenBlockSub { AddThenBlock(#[primary_span] Span), } +#[derive(Diagnostic)] +#[diag(parse_ternary_operator)] +#[help] +pub struct TernaryOperator { + #[primary_span] + pub span: Span, +} + #[derive(Subdiagnostic)] #[suggestion(parse_extra_if_in_let_else, applicability = "maybe-incorrect", code = "")] pub(crate) struct IfExpressionLetSomeSub { @@ -2692,3 +2700,34 @@ pub(crate) struct ExpectedBuiltinIdent { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_static_with_generics)] +pub(crate) struct StaticWithGenerics { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_where_clause_before_const_body)] +pub(crate) struct WhereClauseBeforeConstBody { + #[primary_span] + #[label] + pub span: Span, + #[label(parse_name_label)] + pub name: Span, + #[label(parse_body_label)] + pub body: Span, + #[subdiagnostic] + pub sugg: Option<WhereClauseBeforeConstBodySugg>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct WhereClauseBeforeConstBodySugg { + #[suggestion_part(code = "= {snippet} ")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub right: Span, +} diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs index 9e6d27bf036..b50bb47f297 100644 --- a/compiler/rustc_parse/src/lexer/diagnostics.rs +++ b/compiler/rustc_parse/src/lexer/diagnostics.rs @@ -46,7 +46,7 @@ pub fn report_missing_open_delim( }; err.span_label( unmatch_brace.found_span.shrink_to_lo(), - format!("missing open `{}` for this delimiter", missed_open), + format!("missing open `{missed_open}` for this delimiter"), ); reported_missing_open = true; } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index c6e6b46e455..1931ee5e528 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -9,8 +9,8 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::contains_text_flow_control_chars; use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, StashKey}; use rustc_lexer::unescape::{self, EscapeError, Mode}; -use rustc_lexer::Cursor; use rustc_lexer::{Base, DocStyle, RawStrError}; +use rustc_lexer::{Cursor, LiteralKind}; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, }; @@ -118,6 +118,7 @@ impl<'a> StringReader<'a> { let mut swallow_next_invalid = 0; // Skip trivial (whitespace & comments) tokens loop { + let str_before = self.cursor.as_str(); let token = self.cursor.advance_token(); let start = self.pos; self.pos = self.pos + BytePos(token.len); @@ -165,10 +166,7 @@ impl<'a> StringReader<'a> { continue; } rustc_lexer::TokenKind::Ident => { - let sym = nfc_normalize(self.str_from(start)); - let span = self.mk_sp(start, self.pos); - self.sess.symbol_gallery.insert(sym, span); - token::Ident(sym, false) + self.ident(start) } rustc_lexer::TokenKind::RawIdent => { let sym = nfc_normalize(self.str_from(start + BytePos(2))); @@ -182,10 +180,7 @@ impl<'a> StringReader<'a> { } rustc_lexer::TokenKind::UnknownPrefix => { self.report_unknown_prefix(start); - let sym = nfc_normalize(self.str_from(start)); - let span = self.mk_sp(start, self.pos); - self.sess.symbol_gallery.insert(sym, span); - token::Ident(sym, false) + self.ident(start) } rustc_lexer::TokenKind::InvalidIdent // Do not recover an identifier with emoji if the codepoint is a confusable @@ -203,6 +198,27 @@ impl<'a> StringReader<'a> { .push(span); token::Ident(sym, false) } + // split up (raw) c string literals to an ident and a string literal when edition < 2021. + rustc_lexer::TokenKind::Literal { + kind: kind @ (LiteralKind::CStr { .. } | LiteralKind::RawCStr { .. }), + suffix_start: _, + } if !self.mk_sp(start, self.pos).edition().at_least_rust_2021() => { + let prefix_len = match kind { + LiteralKind::CStr { .. } => 1, + LiteralKind::RawCStr { .. } => 2, + _ => unreachable!(), + }; + + // reset the state so that only the prefix ("c" or "cr") + // was consumed. + let lit_start = start + BytePos(prefix_len); + self.pos = lit_start; + self.cursor = Cursor::new(&str_before[prefix_len as usize..]); + + self.report_unknown_prefix(start); + let prefix_span = self.mk_sp(start, lit_start); + return (Token::new(self.ident(start), prefix_span), preceded_by_whitespace); + } rustc_lexer::TokenKind::Literal { kind, suffix_start } => { let suffix_start = start + BytePos(suffix_start); let (kind, symbol) = self.cook_lexer_literal(start, suffix_start, kind); @@ -317,6 +333,13 @@ impl<'a> StringReader<'a> { } } + fn ident(&self, start: BytePos) -> TokenKind { + let sym = nfc_normalize(self.str_from(start)); + let span = self.mk_sp(start, self.pos); + self.sess.symbol_gallery.insert(sym, span); + token::Ident(sym, false) + } + fn struct_fatal_span_char( &self, from_pos: BytePos, diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 318a2998509..07910113dee 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -198,7 +198,7 @@ impl<'a> TokenTreesReader<'a> { // An unexpected closing delimiter (i.e., there is no // matching opening delimiter). let token_str = token_to_string(&self.token); - let msg = format!("unexpected closing delimiter: `{}`", token_str); + let msg = format!("unexpected closing delimiter: `{token_str}`"); let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg); report_suspicious_mismatch_block( diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 461a34b67db..446472d1294 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -27,7 +27,7 @@ pub(crate) fn emit_unescape_error( lit, span_with_quotes, mode, range, error ); let last_char = || { - let c = lit[range.clone()].chars().rev().next().unwrap(); + let c = lit[range.clone()].chars().next_back().unwrap(); let span = span.with_lo(span.hi() - BytePos(c.len_utf8() as u32)); (c, span) }; @@ -135,7 +135,7 @@ pub(crate) fn emit_unescape_error( "unknown character escape" }; let ec = escaped_char(c); - let mut diag = handler.struct_span_err(span, format!("{}: `{}`", label, ec)); + let mut diag = handler.struct_span_err(span, format!("{label}: `{ec}`")); diag.span_label(span, label); if c == '{' || c == '}' && matches!(mode, Mode::Str | Mode::RawStr) { diag.help( @@ -151,7 +151,7 @@ pub(crate) fn emit_unescape_error( diag.span_suggestion( span_with_quotes, "if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal", - format!("r\"{}\"", lit), + format!("r\"{lit}\""), Applicability::MaybeIncorrect, ); } @@ -180,21 +180,20 @@ pub(crate) fn emit_unescape_error( Mode::RawByteStr => "raw byte string literal", _ => panic!("non-is_byte literal paired with NonAsciiCharInByte"), }; - let mut err = handler.struct_span_err(span, format!("non-ASCII character in {}", desc)); + let mut err = handler.struct_span_err(span, format!("non-ASCII character in {desc}")); let postfix = if unicode_width::UnicodeWidthChar::width(c).unwrap_or(1) == 0 { - format!(" but is {:?}", c) + format!(" but is {c:?}") } else { String::new() }; - err.span_label(span, format!("must be ASCII{}", postfix)); + err.span_label(span, format!("must be ASCII{postfix}")); // Note: the \\xHH suggestions are not given for raw byte string // literals, because they are araw and so cannot use any escapes. if (c as u32) <= 0xFF && mode != Mode::RawByteStr { err.span_suggestion( span, format!( - "if you meant to use the unicode code point for {:?}, use a \\xHH escape", - c + "if you meant to use the unicode code point for {c:?}, use a \\xHH escape" ), format!("\\x{:X}", c as u32), Applicability::MaybeIncorrect, @@ -206,7 +205,7 @@ pub(crate) fn emit_unescape_error( utf8.push(c); err.span_suggestion( span, - format!("if you meant to use the UTF-8 encoding of {:?}, use \\xHH escapes", c), + format!("if you meant to use the UTF-8 encoding of {c:?}, use \\xHH escapes"), utf8.as_bytes() .iter() .map(|b: &u8| format!("\\x{:X}", *b)) diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 829d9693e55..bbfb160ebf7 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -349,7 +349,7 @@ pub(super) fn check_for_substitution( let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count)); let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else { - let msg = format!("substitution character not found for '{}'", ch); + let msg = format!("substitution character not found for '{ch}'"); reader.sess.span_diagnostic.span_bug_no_panic(span, msg); return (None, None); }; diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 25de7808532..6712db26693 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -205,7 +205,7 @@ pub fn stream_to_parser<'a>( stream: TokenStream, subparser_name: Option<&'static str>, ) -> Parser<'a> { - Parser::new(sess, stream, false, subparser_name) + Parser::new(sess, stream, subparser_name) } /// Runs the given subparser `f` on the tokens of the given `attr`'s item. @@ -215,7 +215,7 @@ pub fn parse_in<'a, T>( name: &'static str, mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>, ) -> PResult<'a, T> { - let mut parser = Parser::new(sess, tts, false, Some(name)); + let mut parser = Parser::new(sess, tts, Some(name)); let result = f(&mut parser)?; if parser.token != token::Eof { parser.unexpected()?; @@ -247,7 +247,7 @@ pub fn parse_cfg_attr( match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) { Ok(r) => return Some(r), Err(mut e) => { - e.help(format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP)) + e.help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`")) .note(CFG_ATTR_NOTE_REF) .emit(); } diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index b579da098d8..158ab2a2956 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -107,7 +107,7 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { let tokens = std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1)) .chain((0..self.num_calls).map(|_| { - let token = cursor_snapshot.next(cursor_snapshot.desugar_doc_comments); + let token = cursor_snapshot.next(); (FlatToken::Token(token.0), token.1) })) .take(self.num_calls); @@ -145,13 +145,11 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // another replace range will capture the *replaced* tokens for the inner // range, not the original tokens. for (range, new_tokens) in replace_ranges.into_iter().rev() { - assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range); + assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); // Replace ranges are only allowed to decrease the number of tokens. assert!( range.len() >= new_tokens.len(), - "Range {:?} has greater len than {:?}", - range, - new_tokens + "Range {range:?} has greater len than {new_tokens:?}" ); // Replace any removed tokens with `FlatToken::Empty`. @@ -409,22 +407,19 @@ fn make_token_stream( FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => { let frame_data = stack .pop() - .unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token)); + .unwrap_or_else(|| panic!("Token stack was empty for token: {token:?}")); let (open_delim, open_sp) = frame_data.open_delim_sp.unwrap(); assert_eq!( open_delim, delim, - "Mismatched open/close delims: open={:?} close={:?}", - open_delim, span + "Mismatched open/close delims: open={open_delim:?} close={span:?}" ); let dspan = DelimSpan::from_pair(open_sp, span); let stream = AttrTokenStream::new(frame_data.inner); let delimited = AttrTokenTree::Delimited(dspan, delim, stream); stack .last_mut() - .unwrap_or_else(|| { - panic!("Bottom token frame is missing for token: {:?}", token) - }) + .unwrap_or_else(|| panic!("Bottom token frame is missing for token: {token:?}")) .inner .push(delimited); } @@ -456,7 +451,7 @@ fn make_token_stream( .inner .push(AttrTokenTree::Token(Token::new(unglued_first, first_span), spacing)); } else { - panic!("Unexpected last token {:?}", last_token) + panic!("Unexpected last token {last_token:?}") } } AttrTokenStream::new(final_buf.inner) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 8455803ad0c..e6de51a673c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -14,7 +14,7 @@ use crate::errors::{ PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, - UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, + TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, }; @@ -247,7 +247,7 @@ impl<'a> Parser<'a> { self.sess.span_diagnostic.struct_span_err(sp, m) } - pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<DiagnosticMessage>) -> ! { + pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, m: impl Into<String>) -> ! { self.sess.span_diagnostic.span_bug(sp, m) } @@ -500,6 +500,10 @@ impl<'a> Parser<'a> { // Special-case "expected `;`" errors if expected.contains(&TokenType::Token(token::Semi)) { + if self.prev_token == token::Question && self.maybe_recover_from_ternary_operator() { + return Ok(true); + } + if self.token.span == DUMMY_SP || self.prev_token.span == DUMMY_SP { // Likely inside a macro, can't provide meaningful suggestions. } else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) { @@ -608,13 +612,13 @@ impl<'a> Parser<'a> { if let TokenKind::Ident(prev, _) = &self.prev_token.kind && let TokenKind::Ident(cur, _) = &self.token.kind { - let concat = Symbol::intern(&format!("{}{}", prev, cur)); + let concat = Symbol::intern(&format!("{prev}{cur}")); let ident = Ident::new(concat, DUMMY_SP); if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() { let span = self.prev_token.span.to(self.token.span); err.span_suggestion_verbose( span, - format!("consider removing the space to spell keyword `{}`", concat), + format!("consider removing the space to spell keyword `{concat}`"), concat, Applicability::MachineApplicable, ); @@ -1330,6 +1334,45 @@ impl<'a> Parser<'a> { } } + /// Rust has no ternary operator (`cond ? then : else`). Parse it and try + /// to recover from it if `then` and `else` are valid expressions. Returns + /// whether it was a ternary operator. + pub(super) fn maybe_recover_from_ternary_operator(&mut self) -> bool { + if self.prev_token != token::Question { + return false; + } + + let lo = self.prev_token.span.lo(); + let snapshot = self.create_snapshot_for_diagnostic(); + + if match self.parse_expr() { + Ok(_) => true, + Err(err) => { + err.cancel(); + // The colon can sometimes be mistaken for type + // ascription. Catch when this happens and continue. + self.token == token::Colon + } + } { + if self.eat_noexpect(&token::Colon) { + match self.parse_expr() { + Ok(_) => { + self.sess.emit_err(TernaryOperator { span: self.token.span.with_lo(lo) }); + return true; + } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + } + }; + } + } else { + self.restore_snapshot(snapshot); + }; + + false + } + pub(super) fn maybe_recover_from_bad_type_plus(&mut self, ty: &Ty) -> PResult<'a, ()> { // Do not add `+` to expected tokens. if !self.token.is_like_plus() { @@ -2111,7 +2154,7 @@ impl<'a> Parser<'a> { } _ => ( self.token.span, - format!("expected expression, found {}", super::token_descr(&self.token),), + format!("expected expression, found {}", super::token_descr(&self.token)), ), }; let mut err = self.struct_span_err(span, msg); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7ede4fbc3d9..55f857aa31c 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -238,7 +238,7 @@ impl<'a> Parser<'a> { _ => unreachable!(), } .into(); - let invalid = format!("{}=", &sugg); + let invalid = format!("{sugg}="); self.sess.emit_err(errors::InvalidComparisonOperator { span: sp, invalid: invalid.clone(), @@ -1052,7 +1052,7 @@ impl<'a> Parser<'a> { } components.push(Punct(c)); } else { - panic!("unexpected character in a float token: {:?}", c) + panic!("unexpected character in a float token: {c:?}") } } if !ident_like.is_empty() { @@ -1113,7 +1113,7 @@ impl<'a> Parser<'a> { self.error_unexpected_after_dot(); DestructuredFloat::Error } - _ => panic!("unexpected components in a float token: {:?}", components), + _ => panic!("unexpected components in a float token: {components:?}"), } } @@ -1309,7 +1309,7 @@ impl<'a> Parser<'a> { /// Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> { - if self.token.uninterpolated_span().rust_2018() && self.eat_keyword(kw::Await) { + if self.token.uninterpolated_span().at_least_rust_2018() && self.eat_keyword(kw::Await) { return Ok(self.mk_await_expr(self_arg, lo)); } @@ -1442,8 +1442,8 @@ impl<'a> Parser<'a> { self.parse_expr_let() } else if self.eat_keyword(kw::Underscore) { Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore)) - } else if self.token.uninterpolated_span().rust_2018() { - // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. + } else if self.token.uninterpolated_span().at_least_rust_2018() { + // `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly. if self.check_keyword(kw::Async) { if self.is_async_block() { // Check for `async {` and `async move {`. @@ -2230,7 +2230,7 @@ impl<'a> Parser<'a> { let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; - let asyncness = if self.token.uninterpolated_span().rust_2018() { + let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() { self.parse_asyncness(Case::Sensitive) } else { Async::No @@ -2342,7 +2342,7 @@ impl<'a> Parser<'a> { let ty = if this.eat(&token::Colon) { this.parse_ty()? } else { - this.mk_ty(this.prev_token.span, TyKind::Infer) + this.mk_ty(pat.span, TyKind::Infer) }; Ok(( @@ -3003,7 +3003,8 @@ impl<'a> Parser<'a> { fn is_do_catch_block(&self) -> bool { self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Catch]) - && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace)) + && self + .look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) && !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) } @@ -3013,8 +3014,9 @@ impl<'a> Parser<'a> { fn is_try_block(&self) -> bool { self.token.is_keyword(kw::Try) - && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) - && self.token.uninterpolated_span().rust_2018() + && self + .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) + && self.token.uninterpolated_span().at_least_rust_2018() } /// Parses an `async move? {...}` expression. @@ -3032,10 +3034,14 @@ impl<'a> Parser<'a> { && (( // `async move {` self.is_keyword_ahead(1, &[kw::Move]) - && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace)) + && self.look_ahead(2, |t| { + *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block() + }) ) || ( // `async {` - self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) + self.look_ahead(1, |t| { + *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block() + }) )) } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 8ab38c4fb8b..242c9d332bb 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -49,7 +49,7 @@ impl<'a> Parser<'a> { && self.check_ident() // `Const` followed by IDENT { - return Ok(self.recover_const_param_with_mistyped_const(preceding_attrs, ident)?); + return self.recover_const_param_with_mistyped_const(preceding_attrs, ident); } // Parse optional colon and param bounds. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1470180dea7..1301ed3e388 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -226,9 +226,9 @@ impl<'a> Parser<'a> { } else if self.is_static_global() { // STATIC ITEM self.bump(); // `static` - let m = self.parse_mutability(); - let (ident, ty, expr) = self.parse_item_global(Some(m))?; - (ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr }))) + let mutability = self.parse_mutability(); + let (ident, item) = self.parse_static_item(mutability)?; + (ident, ItemKind::Static(Box::new(item))) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { @@ -236,8 +236,16 @@ impl<'a> Parser<'a> { self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); - let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr }))) + let (ident, generics, ty, expr) = self.parse_const_item()?; + ( + ident, + ItemKind::Const(Box::new(ConstItem { + defaultness: def_(), + generics, + ty, + expr, + })), + ) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -878,6 +886,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { defaultness: Defaultness::Final, + generics: Generics::default(), ty, expr, })) @@ -892,7 +901,7 @@ impl<'a> Parser<'a> { /// Parses a `type` alias with the following grammar: /// ```ebnf - /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; + /// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ; /// ``` /// The `"type"` has already been eaten. fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> { @@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> { Ok(impl_info) } - /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with - /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`. + /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`. /// - /// When `m` is `"const"`, `$ident` may also be `"_"`. - fn parse_item_global( - &mut self, - m: Option<Mutability>, - ) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> { - let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?; + /// ```ebnf + /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ; + /// ``` + fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> { + let ident = self.parse_ident()?; + + if self.token.kind == TokenKind::Lt && self.may_recover() { + let generics = self.parse_generics()?; + self.sess.emit_err(errors::StaticWithGenerics { span: generics.span }); + } - // Parse the type of a `const` or `static mut?` item. - // That is, the `":" $ty` fragment. + // Parse the type of a static item. That is, the `":" $ty` fragment. + // FIXME: This could maybe benefit from `.may_recover()`? let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi)) { - // If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type. (true, false) => self.parse_ty()?, - (colon, _) => self.recover_missing_const_type(colon, m), + // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type. + (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)), + }; + + let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + + self.expect_semi()?; + + Ok((ident, StaticItem { ty, mutability, expr })) + } + + /// Parse a constant item with the prefix `"const"` already parsed. + /// + /// ```ebnf + /// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ; + /// ``` + fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> { + let ident = self.parse_ident_or_underscore()?; + + let mut generics = self.parse_generics()?; + + // Check the span for emptiness instead of the list of parameters in order to correctly + // recognize and subsequently flag empty parameter lists (`<>`) as unstable. + if !generics.span.is_empty() { + self.sess.gated_spans.gate(sym::generic_const_items, generics.span); + } + + // Parse the type of a constant item. That is, the `":" $ty` fragment. + // FIXME: This could maybe benefit from `.may_recover()`? + let ty = match ( + self.eat(&token::Colon), + self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where), + ) { + (true, false) => self.parse_ty()?, + // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type. + (colon, _) => self.recover_missing_global_item_type(colon, None), }; + // Proactively parse a where-clause to be able to provide a good error message in case we + // encounter the item body following it. + let before_where_clause = + if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; + let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + + let after_where_clause = self.parse_where_clause()?; + + // Provide a nice error message if the user placed a where-clause before the item body. + // Users may be tempted to write such code if they are still used to the deprecated + // where-clause location on type aliases and associated types. See also #89122. + if before_where_clause.has_where_token && let Some(expr) = &expr { + self.sess.emit_err(errors::WhereClauseBeforeConstBody { + span: before_where_clause.span, + name: ident.span, + body: expr.span, + sugg: if !after_where_clause.has_where_token { + self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| { + errors::WhereClauseBeforeConstBodySugg { + left: before_where_clause.span.shrink_to_lo(), + snippet: body, + right: before_where_clause.span.shrink_to_hi().to(expr.span), + } + }) + } else { + // FIXME(generic_const_items): Provide a structured suggestion to merge the first + // where-clause into the second one. + None + }, + }); + } + + // Merge the predicates of both where-clauses since either one can be relevant. + // If we didn't parse a body (which is valid for associated consts in traits) and we were + // allowed to recover, `before_where_clause` contains the predicates, otherwise they are + // in `after_where_clause`. Further, both of them might contain predicates iff two + // where-clauses were provided which is syntactically ill-formed but we want to recover from + // it and treat them as one large where-clause. + let mut predicates = before_where_clause.predicates; + predicates.extend(after_where_clause.predicates); + let where_clause = WhereClause { + has_where_token: before_where_clause.has_where_token + || after_where_clause.has_where_token, + predicates, + span: if after_where_clause.has_where_token { + after_where_clause.span + } else { + before_where_clause.span + }, + }; + + if where_clause.has_where_token { + self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span); + } + + generics.where_clause = where_clause; + self.expect_semi()?; - Ok((id, ty, expr)) + + Ok((ident, generics, ty, expr)) } /// We were supposed to parse `":" $ty` but the `:` or the type was missing. /// This means that the type is missing. - fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> { + fn recover_missing_global_item_type( + &mut self, + colon_present: bool, + m: Option<Mutability>, + ) -> P<Ty> { // Construct the error and stash it away with the hope // that typeck will later enrich the error with a type. let kind = match m { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e4d843b7c8b..57778d67098 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -24,7 +24,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; -use rustc_ast::{self as ast, AnonConst, AttrStyle, Const, DelimArgs, Extern}; +use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern}; use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit}; use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; @@ -38,7 +38,7 @@ use rustc_session::parse::ParseSess; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use std::ops::Range; -use std::{cmp, mem, slice}; +use std::{mem, slice}; use thin_vec::ThinVec; use tracing::debug; @@ -138,7 +138,6 @@ pub struct Parser<'a> { // Important: This must only be advanced from `bump` to ensure that // `token_cursor.num_next_calls` is updated properly. token_cursor: TokenCursor, - desugar_doc_comments: bool, /// This field is used to keep track of how many left angle brackets we have seen. This is /// required in order to detect extra leading left angle brackets (`<` characters) and error /// appropriately. @@ -225,8 +224,6 @@ struct TokenCursor { // because it's the outermost token stream which never has delimiters. stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>, - desugar_doc_comments: bool, - // Counts the number of calls to `{,inlined_}next`. num_next_calls: usize, @@ -255,33 +252,26 @@ struct TokenCursor { } impl TokenCursor { - fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) { - self.inlined_next(desugar_doc_comments) + fn next(&mut self) -> (Token, Spacing) { + self.inlined_next() } /// This always-inlined version should only be used on hot code paths. #[inline(always)] - fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) { + fn inlined_next(&mut self) -> (Token, Spacing) { loop { - // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will - // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be - // removed. + // FIXME: we currently don't return `Delimiter::Invisible` open/close delims. To fix + // #67062 we will need to, whereupon the `delim != Delimiter::Invisible` conditions + // below can be removed. if let Some(tree) = self.tree_cursor.next_ref() { match tree { - &TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) { - (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => { - let desugared = self.desugar(attr_style, data, span); - self.tree_cursor.replace_prev_and_rewind(desugared); - // Continue to get the first token of the desugared doc comment. - } - _ => { - debug_assert!(!matches!( - token.kind, - token::OpenDelim(_) | token::CloseDelim(_) - )); - return (token.clone(), spacing); - } - }, + &TokenTree::Token(ref token, spacing) => { + debug_assert!(!matches!( + token.kind, + token::OpenDelim(_) | token::CloseDelim(_) + )); + return (token.clone(), spacing); + } &TokenTree::Delimited(sp, delim, ref tts) => { let trees = tts.clone().into_trees(); self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp)); @@ -304,52 +294,6 @@ impl TokenCursor { } } } - - // Desugar a doc comment into something like `#[doc = r"foo"]`. - fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> Vec<TokenTree> { - // Searches for the occurrences of `"#*` and returns the minimum number of `#`s - // required to wrap the text. E.g. - // - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0) - // - `abc "d"` is wrapped as `r#"abc "d""#` (num_of_hashes = 1) - // - `abc "##d##"` is wrapped as `r###"abc ##"d"##"###` (num_of_hashes = 3) - let mut num_of_hashes = 0; - let mut count = 0; - for ch in data.as_str().chars() { - count = match ch { - '"' => 1, - '#' if count > 0 => count + 1, - _ => 0, - }; - num_of_hashes = cmp::max(num_of_hashes, count); - } - - // `/// foo` becomes `doc = r"foo"`. - let delim_span = DelimSpan::from_single(span); - let body = TokenTree::Delimited( - delim_span, - Delimiter::Bracket, - [ - TokenTree::token_alone(token::Ident(sym::doc, false), span), - TokenTree::token_alone(token::Eq, span), - TokenTree::token_alone( - TokenKind::lit(token::StrRaw(num_of_hashes), data, None), - span, - ), - ] - .into_iter() - .collect::<TokenStream>(), - ); - - if attr_style == AttrStyle::Inner { - vec![ - TokenTree::token_alone(token::Pound, span), - TokenTree::token_alone(token::Not, span), - body, - ] - } else { - vec![TokenTree::token_alone(token::Pound, span), body] - } - } } #[derive(Debug, Clone, PartialEq)] @@ -368,7 +312,7 @@ impl TokenType { fn to_string(&self) -> String { match self { TokenType::Token(t) => format!("`{}`", pprust::token_kind_to_string(t)), - TokenType::Keyword(kw) => format!("`{}`", kw), + TokenType::Keyword(kw) => format!("`{kw}`"), TokenType::Operator => "an operator".to_string(), TokenType::Lifetime => "lifetime".to_string(), TokenType::Ident => "identifier".to_string(), @@ -438,14 +382,13 @@ pub(super) fn token_descr(token: &Token) -> String { TokenDescription::DocComment => "doc comment", }); - if let Some(kind) = kind { format!("{} `{}`", kind, name) } else { format!("`{}`", name) } + if let Some(kind) = kind { format!("{kind} `{name}`") } else { format!("`{name}`") } } impl<'a> Parser<'a> { pub fn new( sess: &'a ParseSess, - tokens: TokenStream, - desugar_doc_comments: bool, + stream: TokenStream, subparser_name: Option<&'static str>, ) -> Self { let mut parser = Parser { @@ -457,13 +400,11 @@ impl<'a> Parser<'a> { restrictions: Restrictions::empty(), expected_tokens: Vec::new(), token_cursor: TokenCursor { - tree_cursor: tokens.into_trees(), + tree_cursor: stream.into_trees(), stack: Vec::new(), num_next_calls: 0, - desugar_doc_comments, break_last_token: false, }, - desugar_doc_comments, unmatched_angle_bracket_count: 0, max_angle_bracket_count: 0, last_unexpected_token_span: None, @@ -923,7 +864,7 @@ impl<'a> Parser<'a> { expect_err .span_suggestion_short( sp, - format!("missing `{}`", token_str), + format!("missing `{token_str}`"), token_str, Applicability::MaybeIncorrect, ) @@ -1107,7 +1048,7 @@ impl<'a> Parser<'a> { pub fn bump(&mut self) { // Note: destructuring here would give nicer code, but it was found in #96210 to be slower // than `.0`/`.1` access. - let mut next = self.token_cursor.inlined_next(self.desugar_doc_comments); + let mut next = self.token_cursor.inlined_next(); self.token_cursor.num_next_calls += 1; // We've retrieved an token from the underlying // cursor, so we no longer need to worry about @@ -1157,7 +1098,7 @@ impl<'a> Parser<'a> { let mut i = 0; let mut token = Token::dummy(); while i < dist { - token = cursor.next(/* desugar_doc_comments */ false).0; + token = cursor.next().0; if matches!( token.kind, token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) @@ -1166,7 +1107,7 @@ impl<'a> Parser<'a> { } i += 1; } - return looker(&token); + looker(&token) } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one. @@ -1210,7 +1151,8 @@ impl<'a> Parser<'a> { fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const { // Avoid const blocks and const closures to be parsed as const items if (self.check_const_closure() == is_closure) - && self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) + && !self + .look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_whole_block()) && self.eat_keyword_case(kw::Const, case) { Const::Yes(self.prev_token.uninterpolated_span()) diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index fdf36517847..fed16278db5 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -8,7 +8,6 @@ use crate::errors::{ TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, }; -use crate::fluent_generated as fluent; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; @@ -214,41 +213,25 @@ impl<'a> Parser<'a> { if let PatKind::Or(pats) = &pat.kind { let span = pat.span; - - if trailing_vert { - // We already emitted an error and suggestion to remove the trailing vert. Don't - // emit again. - - // FIXME(#100717): pass `TopLevelOrPatternNotAllowed::* { sub: None }` to - // `delay_span_bug()` instead of fluent message - self.sess.span_diagnostic.delay_span_bug( - span, - match syntax_loc { - PatternLocation::LetBinding => { - fluent::parse_or_pattern_not_allowed_in_let_binding - } - PatternLocation::FunctionParameter => { - fluent::parse_or_pattern_not_allowed_in_fn_parameters - } - }, - ); + let pat = pprust::pat_to_string(&pat); + let sub = if pats.len() == 1 { + Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span, pat }) } else { - let pat = pprust::pat_to_string(&pat); - let sub = if pats.len() == 1 { - Some(TopLevelOrPatternNotAllowedSugg::RemoveLeadingVert { span, pat }) - } else { - Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat }) - }; + Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat }) + }; - self.sess.emit_err(match syntax_loc { - PatternLocation::LetBinding => { - TopLevelOrPatternNotAllowed::LetBinding { span, sub } - } - PatternLocation::FunctionParameter => { - TopLevelOrPatternNotAllowed::FunctionParameter { span, sub } - } - }); + let mut err = self.sess.create_err(match syntax_loc { + PatternLocation::LetBinding => { + TopLevelOrPatternNotAllowed::LetBinding { span, sub } + } + PatternLocation::FunctionParameter => { + TopLevelOrPatternNotAllowed::FunctionParameter { span, sub } + } + }); + if trailing_vert { + err.delay_as_bug(); } + err.emit(); } Ok((pat, colon)) @@ -445,7 +428,7 @@ impl<'a> Parser<'a> { ); let mut err = self_.struct_span_err(self_.token.span, msg); - err.span_label(self_.token.span, format!("expected {}", expected)); + err.span_label(self_.token.span, format!("expected {expected}")); err }); PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit))) @@ -681,7 +664,7 @@ impl<'a> Parser<'a> { let msg = format!("expected {}, found {}", expected, super::token_descr(&self.token)); let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, format!("expected {}", expected)); + err.span_label(self.token.span, format!("expected {expected}")); let sp = self.sess.source_map().start_point(self.token.span); if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&sp) { @@ -994,7 +977,7 @@ impl<'a> Parser<'a> { break; } let token_str = super::token_descr(&self.token); - let msg = format!("expected `}}`, found {}", token_str); + let msg = format!("expected `}}`, found {token_str}"); let mut err = self.struct_span_err(self.token.span, msg); err.span_label(self.token.span, "expected `}`"); diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index feb7e829caf..445516c03a1 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -679,7 +679,7 @@ impl<'a> Parser<'a> { ); err.span_suggestion( eq.to(before_next), - format!("remove the `=` if `{}` is a type", ident), + format!("remove the `=` if `{ident}` is a type"), "", Applicability::MaybeIncorrect, ) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 9fcf51a04ec..1cdf2efa764 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -300,7 +300,7 @@ impl<'a> Parser<'a> { Ok(ty) => (None, Some(ty)), Err(mut err) => { if let Ok(snip) = self.span_to_snippet(pat.span) { - err.span_label(pat.span, format!("while parsing the type for `{}`", snip)); + err.span_label(pat.span, format!("while parsing the type for `{snip}`")); } // we use noexpect here because we don't actually expect Eq to be here // but we are still checking for it in order to be able to handle it if @@ -502,7 +502,7 @@ impl<'a> Parser<'a> { fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> { let tok = super::token_descr(&self.token); - let msg = format!("expected `{{`, found {}", tok); + let msg = format!("expected `{{`, found {tok}"); Err(self.error_block_no_opening_brace_msg(Cow::from(msg))) } @@ -638,10 +638,9 @@ impl<'a> Parser<'a> { e.span_suggestion( sp.with_hi(sp.lo() + BytePos(marker.len() as u32)), format!( - "add a space before `{}` to use a regular comment", - doc_comment_marker, + "add a space before `{doc_comment_marker}` to use a regular comment", ), - format!("{} {}", comment_marker, doc_comment_marker), + format!("{comment_marker} {doc_comment_marker}"), Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index a29b696aea8..3bb50b05aa3 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -608,7 +608,7 @@ impl<'a> Parser<'a> { /// Is a `dyn B0 + ... + Bn` type allowed here? fn is_explicit_dyn_type(&mut self) -> bool { self.check_keyword(kw::Dyn) - && (self.token.uninterpolated_span().rust_2018() + && (self.token.uninterpolated_span().at_least_rust_2018() || self.look_ahead(1, |t| { (t.can_begin_bound() || t.kind == TokenKind::BinOp(token::Star)) && !can_continue_type_after_non_fn_ident(t) diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 928fdce313d..2011083019c 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -157,15 +157,15 @@ fn emit_malformed_attribute( matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench) }; - let error_msg = format!("malformed `{}` attribute input", name); + let error_msg = format!("malformed `{name}` attribute input"); let mut msg = "attribute must be of the form ".to_owned(); let mut suggestions = vec![]; let mut first = true; let inner = if style == ast::AttrStyle::Inner { "!" } else { "" }; if template.word { first = false; - let code = format!("#{}[{}]", inner, name); - msg.push_str(&format!("`{}`", &code)); + let code = format!("#{inner}[{name}]"); + msg.push_str(&format!("`{code}`")); suggestions.push(code); } if let Some(descr) = template.list { @@ -173,16 +173,16 @@ fn emit_malformed_attribute( msg.push_str(" or "); } first = false; - let code = format!("#{}[{}({})]", inner, name, descr); - msg.push_str(&format!("`{}`", &code)); + let code = format!("#{inner}[{name}({descr})]"); + msg.push_str(&format!("`{code}`")); suggestions.push(code); } if let Some(descr) = template.name_value_str { if !first { msg.push_str(" or "); } - let code = format!("#{}[{} = \"{}\"]", inner, name, descr); - msg.push_str(&format!("`{}`", &code)); + let code = format!("#{inner}[{name} = \"{descr}\"]"); + msg.push_str(&format!("`{code}`")); suggestions.push(code); } if should_warn(name) { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 7de84db211e..88452ccdf05 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -109,6 +109,8 @@ pub struct Argument<'a> { pub struct FormatSpec<'a> { /// Optionally specified character to fill alignment with. pub fill: Option<char>, + /// Span of the optionally specified fill character. + pub fill_span: Option<InnerSpan>, /// Optionally specified alignment. pub align: Alignment, /// The `+` or `-` flag. @@ -264,7 +266,7 @@ impl<'a> Iterator for Parser<'a> { Some(String(self.string(pos + 1))) } else { let arg = self.argument(lbrace_end); - if let Some(rbrace_pos) = self.must_consume('}') { + if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { if self.is_source_literal { let lbrace_byte_pos = self.to_span_index(pos); let rbrace_byte_pos = self.to_span_index(rbrace_pos); @@ -450,69 +452,51 @@ impl<'a> Parser<'a> { /// Forces consumption of the specified character. If the character is not /// found, an error is emitted. - fn must_consume(&mut self, c: char) -> Option<usize> { + fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option<usize> { self.ws(); - if let Some(&(pos, maybe)) = self.cur.peek() { - if c == maybe { + let pos; + let description; + + if let Some(&(peek_pos, maybe)) = self.cur.peek() { + if maybe == '}' { self.cur.next(); - Some(pos) - } else { - let pos = self.to_span_index(pos); - let description = format!("expected `'}}'`, found `{maybe:?}`"); - let label = "expected `}`".to_owned(); - let (note, secondary_label) = if c == '}' { - ( - Some( - "if you intended to print `{`, you can escape it using `{{`".to_owned(), - ), - self.last_opening_brace - .map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - } else { - (None, None) - }; - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - should_be_replaced_with_positional_argument: false, - }); - None + return Some(peek_pos); } + + pos = peek_pos; + description = format!("expected `'}}'`, found `{maybe:?}`"); } else { - let description = format!("expected `{c:?}` but string was terminated"); + description = "expected `'}'` but string was terminated".to_owned(); // point at closing `"` - let pos = self.input.len() - if self.append_newline { 1 } else { 0 }; - let pos = self.to_span_index(pos); - if c == '}' { - let label = format!("expected `{c:?}`"); - let (note, secondary_label) = if c == '}' { - ( - Some( - "if you intended to print `{`, you can escape it using `{{`".to_owned(), - ), - self.last_opening_brace - .map(|sp| ("because of this opening brace".to_owned(), sp)), - ) - } else { - (None, None) - }; - self.errors.push(ParseError { - description, - note, - label, - span: pos.to(pos), - secondary_label, - should_be_replaced_with_positional_argument: false, - }); - } else { - self.err(description, format!("expected `{c:?}`"), pos.to(pos)); - } - None + pos = self.input.len() - if self.append_newline { 1 } else { 0 }; } + + let pos = self.to_span_index(pos); + + let label = "expected `'}'`".to_owned(); + let (note, secondary_label) = if arg.format.fill == Some('}') { + ( + Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()), + arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)), + ) + } else { + ( + Some("if you intended to print `{`, you can escape it using `{{`".to_owned()), + self.last_opening_brace.map(|sp| ("because of this opening brace".to_owned(), sp)), + ) + }; + + self.errors.push(ParseError { + description, + note, + label, + span: pos.to(pos), + secondary_label, + should_be_replaced_with_positional_argument: false, + }); + + None } /// Consumes all whitespace characters until the first non-whitespace character @@ -608,6 +592,7 @@ impl<'a> Parser<'a> { fn format(&mut self) -> FormatSpec<'a> { let mut spec = FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -625,9 +610,10 @@ impl<'a> Parser<'a> { } // fill character - if let Some(&(_, c)) = self.cur.peek() { + if let Some(&(idx, c)) = self.cur.peek() { if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) { spec.fill = Some(c); + spec.fill_span = Some(self.span(idx, idx + 1)); self.cur.next(); } } @@ -722,6 +708,7 @@ impl<'a> Parser<'a> { fn inline_asm(&mut self) -> FormatSpec<'a> { let mut spec = FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 45314e2fb55..0c594f9104c 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -9,6 +9,7 @@ fn same(fmt: &'static str, p: &[Piece<'static>]) { fn fmtdflt() -> FormatSpec<'static> { return FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -128,6 +129,7 @@ fn format_type() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -152,6 +154,7 @@ fn format_align_fill() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignRight, sign: None, alternate: false, @@ -173,6 +176,7 @@ fn format_align_fill() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: Some('0'), + fill_span: Some(InnerSpan::new(4, 5)), align: AlignLeft, sign: None, alternate: false, @@ -194,6 +198,7 @@ fn format_align_fill() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: Some('*'), + fill_span: Some(InnerSpan::new(4, 5)), align: AlignLeft, sign: None, alternate: false, @@ -218,6 +223,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -239,6 +245,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -260,6 +267,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 3 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -281,6 +289,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -302,6 +311,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -323,6 +333,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -344,6 +355,7 @@ fn format_counts() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, @@ -368,6 +380,7 @@ fn format_flags() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: Some(Sign::Minus), alternate: false, @@ -389,6 +402,7 @@ fn format_flags() { position_span: InnerSpan { start: 2, end: 2 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: Some(Sign::Plus), alternate: true, @@ -415,6 +429,7 @@ fn format_mixture() { position_span: InnerSpan { start: 7, end: 8 }, format: FormatSpec { fill: None, + fill_span: None, align: AlignUnknown, sign: None, alternate: false, diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 0aa3d6265dc..5b3cc5d99cc 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -211,6 +211,17 @@ passes_doc_keyword_not_mod = passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks +passes_doc_masked_not_extern_crate_self = + this attribute cannot be applied to an `extern crate self` item + .label = not applicable on `extern crate self` items + .extern_crate_self_label = `extern crate self` defined here + +passes_doc_masked_only_extern_crate = + this attribute can only be applied to an `extern crate` item + .label = only applicable on `extern crate` items + .not_an_extern_crate_label = not an `extern crate` item + .note = read <https://doc.rust-lang.org/unstable-book/language-features/doc-masked.html> for more information + passes_doc_test_literal = `#![doc(test(...)]` does not take a literal passes_doc_test_takes_list = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4d7ebe3fefe..cbb030958c6 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -878,6 +878,44 @@ impl CheckAttrVisitor<'_> { } } + fn check_doc_masked( + &self, + attr: &Attribute, + meta: &NestedMetaItem, + hir_id: HirId, + target: Target, + ) -> bool { + if target != Target::ExternCrate { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocMaskedOnlyExternCrate { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), + }, + ); + return false; + } + + if self.tcx.extern_mod_stmt_cnum(hir_id.owner).is_none() { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocMaskedNotExternCrateSelf { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), + }, + ); + return false; + } + + true + } + /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid. fn check_attr_not_crate_level( &self, @@ -1048,6 +1086,17 @@ impl CheckAttrVisitor<'_> { is_valid = false; } + sym::masked + if !self.check_doc_masked( + attr, + meta, + hir_id, + target, + ) => + { + is_valid = false; + } + // no_default_passes: deprecated // passes: deprecated // plugins: removed, but rustdoc warns about it itself diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index fc437c429fb..e70817d7b7c 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -157,10 +157,8 @@ impl<'tcx> CheckConstVisitor<'tcx> { // is a pretty narrow case, however. if tcx.sess.is_nightly_build() { for gate in missing_secondary { - let note = format!( - "add `#![feature({})]` to the crate attributes to enable", - gate, - ); + let note = + format!("add `#![feature({gate})]` to the crate attributes to enable",); err.help(note); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index eae13f86049..4f5514372d1 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -267,6 +267,25 @@ pub struct DocInlineOnlyUse { pub item_span: Option<Span>, } +#[derive(LintDiagnostic)] +#[diag(passes_doc_masked_only_extern_crate)] +#[note] +pub struct DocMaskedOnlyExternCrate { + #[label] + pub attr_span: Span, + #[label(passes_not_an_extern_crate_label)] + pub item_span: Option<Span>, +} + +#[derive(LintDiagnostic)] +#[diag(passes_doc_masked_not_extern_crate_self)] +pub struct DocMaskedNotExternCrateSelf { + #[label] + pub attr_span: Span, + #[label(passes_extern_crate_self_label)] + pub item_span: Option<Span>, +} + #[derive(Diagnostic)] #[diag(passes_doc_attr_not_crate_level)] pub struct DocAttrNotCrateLevel<'a> { diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 363e1743677..f825363ae39 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -89,9 +89,8 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { self.error(|| { format!( - "ItemLocalIds not assigned densely in {}. \ - Max ItemLocalId = {}, missing IDs = {:#?}; seen IDs = {:#?}", - pretty_owner, max, missing_items, seen_items + "ItemLocalIds not assigned densely in {pretty_owner}. \ + Max ItemLocalId = {max}, missing IDs = {missing_items:#?}; seen IDs = {seen_items:#?}" ) }); } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 6c748147abe..5aa8aef6a85 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -126,12 +126,12 @@ impl<'k> StatCollector<'k> { let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum(); - eprintln!("{} {}", prefix, title); + eprintln!("{prefix} {title}"); eprintln!( "{} {:<18}{:>18}{:>14}{:>14}", prefix, "Name", "Accumulated Size", "Count", "Item Size" ); - eprintln!("{} ----------------------------------------------------------------", prefix); + eprintln!("{prefix} ----------------------------------------------------------------"); let percent = |m, n| (m * 100) as f64 / n as f64; @@ -163,9 +163,9 @@ impl<'k> StatCollector<'k> { } } } - eprintln!("{} ----------------------------------------------------------------", prefix); + eprintln!("{prefix} ----------------------------------------------------------------"); eprintln!("{} {:<18}{:>10}", prefix, "Total", to_readable_str(total_size)); - eprintln!("{}", prefix); + eprintln!("{prefix}"); } } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 89c6704e416..15757a0f1ad 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -605,7 +605,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { for var_idx in 0..self.ir.var_kinds.len() { let var = Variable::from(var_idx); if test(var) { - write!(wr, " {:?}", var)?; + write!(wr, " {var:?}")?; } } Ok(()) diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 160528e4074..7dec5b0acc8 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -236,7 +236,7 @@ impl<'tcx> ReachableContext<'tcx> { // Reachable constants will be inlined into other crates // unconditionally, so we need to make sure that their // contents are also reachable. - hir::ItemKind::Const(_, init) | hir::ItemKind::Static(_, _, init) => { + hir::ItemKind::Const(_, _, init) | hir::ItemKind::Static(_, _, init) => { self.visit_nested_body(init); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index e2827252509..5fa780a372b 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1941,7 +1941,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> { let reexported_at_vis = effective_vis.at_level(Level::Reexported); let reachable_at_vis = effective_vis.at_level(Level::Reachable); - if reexported_at_vis != reachable_at_vis { + if reachable_at_vis.is_public() && reexported_at_vis != reachable_at_vis { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); let span = self.tcx.def_span(def_id.to_def_id()); self.tcx.emit_spanned_lint( @@ -1973,10 +1973,6 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> { AssocItemKind::Type => (self.tcx.defaultness(def_id).has_value(), true), }; - if is_assoc_ty { - self.check_unnameable(def_id, self.get(def_id)); - } - check.in_assoc_ty = is_assoc_ty; check.generics().predicates(); if check_ty { diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 12a3f2ac802..def6ac280b8 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -183,7 +183,7 @@ pub(super) fn encode_all_query_results<'tcx>( encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex, ) { - for encode in super::ENCODE_QUERY_RESULTS.iter().copied().filter_map(|e| e) { + for encode in super::ENCODE_QUERY_RESULTS.iter().copied().flatten() { encode(tcx, encoder, query_result_index); } } diff --git a/compiler/rustc_query_system/messages.ftl b/compiler/rustc_query_system/messages.ftl index 49b423d1ade..d5fed8fe179 100644 --- a/compiler/rustc_query_system/messages.ftl +++ b/compiler/rustc_query_system/messages.ftl @@ -1,4 +1,5 @@ query_system_cycle = cycle detected when {$stack_bottom} + .note = see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index c9e80a6d9bc..b87757a3e1a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -557,7 +557,7 @@ impl<K: DepKind> DepGraph<K> { result, prev_index, hash_result, - |value| format!("{:?}", value), + |value| format!("{value:?}"), ); #[cfg(debug_assertions)] @@ -1433,7 +1433,7 @@ pub(crate) fn print_markframe_trace<K: DepKind>( let mut current = frame; while let Some(frame) = current { let node = data.previous.index_to_node(frame.index); - eprintln!("#{i} {:?}", node); + eprintln!("#{i} {node:?}"); current = frame.parent; i += 1; } diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index cf2f04c7486..e49e78cc7c4 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -57,6 +57,8 @@ pub struct Cycle { pub alias: Option<Alias>, #[subdiagnostic] pub cycle_usage: Option<CycleUsage>, + #[note] + pub note_span: (), } #[derive(Diagnostic)] diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index f45f7ca5da6..d2140161f1d 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -13,6 +13,7 @@ use rustc_session::Session; use rustc_span::Span; use std::hash::Hash; +use std::io::Write; use std::num::NonZeroU64; #[cfg(parallel_compiler)] @@ -607,6 +608,7 @@ pub(crate) fn report_cycle<'a, D: DepKind>( alias, cycle_usage: cycle_usage, stack_count, + note_span: (), }; cycle_diag.into_diagnostic(&sess.parse_sess.span_diagnostic) @@ -617,30 +619,50 @@ pub fn print_query_stack<Qcx: QueryContext>( mut current_query: Option<QueryJobId>, handler: &Handler, num_frames: Option<usize>, + mut file: Option<std::fs::File>, ) -> usize { // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `Handler` may be in a weird // state if it was responsible for triggering the panic. - let mut i = 0; + let mut count_printed = 0; + let mut count_total = 0; let query_map = qcx.try_collect_active_jobs(); + if let Some(ref mut file) = file { + let _ = writeln!(file, "\n\nquery stack during panic:"); + } while let Some(query) = current_query { - if Some(i) == num_frames { - break; - } let Some(query_info) = query_map.as_ref().and_then(|map| map.get(&query)) else { break; }; - let mut diag = Diagnostic::new( - Level::FailureNote, - format!("#{} [{:?}] {}", i, query_info.query.dep_kind, query_info.query.description), - ); - diag.span = query_info.job.span.into(); - handler.force_print_diagnostic(diag); + if Some(count_printed) < num_frames || num_frames.is_none() { + // Only print to stderr as many stack frames as `num_frames` when present. + let mut diag = Diagnostic::new( + Level::FailureNote, + format!( + "#{} [{:?}] {}", + count_printed, query_info.query.dep_kind, query_info.query.description + ), + ); + diag.span = query_info.job.span.into(); + handler.force_print_diagnostic(diag); + count_printed += 1; + } + + if let Some(ref mut file) = file { + let _ = writeln!( + file, + "#{} [{:?}] {}", + count_total, query_info.query.dep_kind, query_info.query.description + ); + } current_query = query_info.job.parent; - i += 1; + count_total += 1; } - i + if let Some(ref mut file) = file { + let _ = writeln!(file, "end of query stack"); + } + count_printed } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index ff63e4e33cb..2f432799022 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -41,6 +41,7 @@ impl<'a, Id: Into<DefId>> ToNameBinding<'a> arenas.alloc_name_binding(NameBindingData { kind: NameBindingKind::Module(self.0), ambiguity: None, + warn_ambiguity: false, vis: self.1.to_def_id(), span: self.2, expansion: self.3, @@ -53,6 +54,7 @@ impl<'a, Id: Into<DefId>> ToNameBinding<'a> for (Res, ty::Visibility<Id>, Span, arenas.alloc_name_binding(NameBindingData { kind: NameBindingKind::Res(self.0), ambiguity: None, + warn_ambiguity: false, vis: self.1.to_def_id(), span: self.2, expansion: self.3, @@ -69,7 +71,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { { let binding = def.to_name_binding(self.arenas); let key = self.new_disambiguated_key(ident, ns); - if let Err(old_binding) = self.try_define(parent, key, binding) { + if let Err(old_binding) = self.try_define(parent, key, binding, false) { self.report_conflict(parent, ident, ns, old_binding, binding); } } @@ -999,7 +1001,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { allow_shadowing: bool, ) { if self.r.macro_use_prelude.insert(name, binding).is_some() && !allow_shadowing { - let msg = format!("`{}` is already in scope", name); + let msg = format!("`{name}` is already in scope"); let note = "macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)"; self.r.tcx.sess.struct_span_err(span, msg).note(note).emit(); diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index dc35c8b176f..7dbbd4c34ea 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -362,7 +362,7 @@ impl Resolver<'_, '_> { let mut span_snippets = spans .iter() .filter_map(|s| match tcx.sess.source_map().span_to_snippet(*s) { - Ok(s) => Some(format!("`{}`", s)), + Ok(s) => Some(format!("`{s}`")), _ => None, }) .collect::<Vec<String>>(); @@ -440,7 +440,7 @@ impl Resolver<'_, '_> { // If we are not in Rust 2018 edition, then we don't make any further // suggestions. - if !tcx.sess.rust_2018() { + if !tcx.sess.at_least_rust_2018() { continue; } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d3dcdfa4275..cd1a9b934cf 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -5,10 +5,8 @@ use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{ - pluralize, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, -}; -use rustc_errors::{struct_span_err, SuggestionStyle}; +use rustc_errors::{pluralize, report_ambiguity_error, struct_span_err, SuggestionStyle}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; @@ -17,8 +15,9 @@ use rustc_hir::PrimTy; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE; +use rustc_session::lint::builtin::AMBIGUOUS_GLOB_IMPORTS; use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS; -use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_session::lint::{AmbiguityErrorDiag, BuiltinLintDiagnostics}; use rustc_session::Session; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; @@ -135,7 +134,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } for ambiguity_error in &self.ambiguity_errors { - self.report_ambiguity_error(ambiguity_error); + let diag = self.ambiguity_diagnostics(ambiguity_error); + if ambiguity_error.warning { + let NameBindingKind::Import { import, .. } = ambiguity_error.b1.0.kind else { + unreachable!() + }; + self.lint_buffer.buffer_lint_with_diagnostic( + AMBIGUOUS_GLOB_IMPORTS, + import.root_id, + ambiguity_error.ident.span, + diag.msg.to_string(), + BuiltinLintDiagnostics::AmbiguousGlobImports { diag }, + ); + } else { + let mut err = struct_span_err!(self.tcx.sess, diag.span, E0659, "{}", &diag.msg); + report_ambiguity_error(&mut err, diag); + err.emit(); + } } let mut reported_spans = FxHashSet::default(); @@ -228,7 +243,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { (TypeNS, _) => "type", }; - let msg = format!("the name `{}` is defined multiple times", name); + let msg = format!("the name `{name}` is defined multiple times"); let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { (true, true) => struct_span_err!(self.tcx.sess, span, E0259, "{}", msg), @@ -250,11 +265,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { container )); - err.span_label(span, format!("`{}` re{} here", name, new_participle)); + err.span_label(span, format!("`{name}` re{new_participle} here")); if !old_binding.span.is_dummy() && old_binding.span != span { err.span_label( self.tcx.sess.source_map().guess_head_span(old_binding.span), - format!("previous {} of the {} `{}` here", old_noun, old_kind, name), + format!("previous {old_noun} of the {old_kind} `{name}` here"), ); } @@ -343,15 +358,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { binding_span: Span, ) { let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { - format!("Other{}", name) + format!("Other{name}") } else { - format!("other_{}", name) + format!("other_{name}") }; let mut suggestion = None; match import.kind { ImportKind::Single { type_ns_only: true, .. } => { - suggestion = Some(format!("self as {}", suggested_name)) + suggestion = Some(format!("self as {suggested_name}")) } ImportKind::Single { source, .. } => { if let Some(pos) = @@ -587,11 +602,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let sugg_msg = "try using a local generic parameter instead"; let name = self.tcx.item_name(def_id); let (span, snippet) = if span.is_empty() { - let snippet = format!("<{}>", name); + let snippet = format!("<{name}>"); (span, snippet) } else { let span = sm.span_through_char(span, '<').shrink_to_hi(); - let snippet = format!("{}, ", name); + let snippet = format!("{name}, "); (span, snippet) }; // Suggest the modification to the user @@ -652,7 +667,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { name, ); for sp in target_sp { - err.span_label(sp, format!("pattern doesn't bind `{}`", name)); + err.span_label(sp, format!("pattern doesn't bind `{name}`")); } for sp in origin_sp { err.span_label(sp, "variable not in all patterns"); @@ -679,8 +694,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if import_suggestions.is_empty() { let help_msg = format!( "if you meant to match on a variant or a `const` item, consider \ - making the path in the pattern qualified: `path::to::ModOrType::{}`", - name, + making the path in the pattern qualified: `path::to::ModOrType::{name}`", ); err.span_help(span, help_msg); } @@ -938,8 +952,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let mut err = self.tcx.sess.struct_span_err_with_code( span, format!( - "item `{}` is an associated {}, which doesn't match its trait `{}`", - name, kind, trait_path, + "item `{name}` is an associated {kind}, which doesn't match its trait `{trait_path}`", ), code, ); @@ -1203,7 +1216,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if filter_fn(res) { // create the path let mut segms = path_segments.clone(); - if lookup_ident.span.rust_2018() { + if lookup_ident.span.at_least_rust_2018() { // crate-local absolute paths start with `crate::` in edition 2018 // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) segms.insert(0, ast::PathSegment::from_ident(crate_name)); @@ -1268,7 +1281,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { path_segments.push(ast::PathSegment::from_ident(ident)); let is_extern_crate_that_also_appears_in_prelude = - name_binding.is_extern_crate() && lookup_ident.span.rust_2018(); + name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); if !is_extern_crate_that_also_appears_in_prelude { // add the module to the lookup @@ -1315,7 +1328,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { &filter_fn, ); - if lookup_ident.span.rust_2018() { + if lookup_ident.span.at_least_rust_2018() { let extern_prelude_names = self.extern_prelude.clone(); for (ident, _) in extern_prelude_names.into_iter() { if ident.span.from_expansion() { @@ -1395,7 +1408,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let head_span = source_map.guess_head_span(span); err.subdiagnostic(ConsiderAddingADerive { span: head_span.shrink_to_lo(), - suggestion: format!("#[derive(Default)]\n") + suggestion: "#[derive(Default)]\n".to_string(), }); } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { @@ -1412,10 +1425,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { "a function-like macro".to_string() } Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { - format!("an attribute: `#[{}]`", ident) + format!("an attribute: `#[{ident}]`") } Res::Def(DefKind::Macro(MacroKind::Derive), _) => { - format!("a derive macro: `#[derive({})]`", ident) + format!("a derive macro: `#[derive({ident})]`") } Res::ToolMod => { // Don't confuse the user with tool modules. @@ -1436,7 +1449,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if !import.span.is_dummy() { err.span_note( import.span, - format!("`{}` is imported here, but it is {}", ident, desc), + format!("`{ident}` is imported here, but it is {desc}"), ); // Silence the 'unused import' warning we might get, // since this diagnostic already covers that import. @@ -1444,7 +1457,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { return; } } - err.note(format!("`{}` is in scope, but it is {}", ident, desc)); + err.note(format!("`{ident}` is in scope, but it is {desc}")); return; } } @@ -1540,20 +1553,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn report_ambiguity_error(&self, ambiguity_error: &AmbiguityError<'_>) { - let AmbiguityError { kind, ident, b1, b2, misc1, misc2 } = *ambiguity_error; + fn ambiguity_diagnostics(&self, ambiguity_error: &AmbiguityError<'_>) -> AmbiguityErrorDiag { + let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error; let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { // We have to print the span-less alternative first, otherwise formatting looks bad. (b2, b1, misc2, misc1, true) } else { (b1, b2, misc1, misc2, false) }; - - let mut err = struct_span_err!(self.tcx.sess, ident.span, E0659, "`{ident}` is ambiguous"); - err.span_label(ident.span, "ambiguous name"); - err.note(format!("ambiguous because of {}", kind.descr())); - - let mut could_refer_to = |b: NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| { + let could_refer_to = |b: NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| { let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude); let note_msg = format!("`{ident}` could{also} refer to {what}"); @@ -1568,7 +1576,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { "consider adding an explicit import of `{ident}` to disambiguate" )) } - if b.is_extern_crate() && ident.span.rust_2018() { + if b.is_extern_crate() && ident.span.at_least_rust_2018() { help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) } match misc { @@ -1579,16 +1587,35 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { AmbiguityErrorMisc::FromPrelude | AmbiguityErrorMisc::None => {} } - err.span_note(b.span, note_msg); - for (i, help_msg) in help_msgs.iter().enumerate() { - let or = if i == 0 { "" } else { "or " }; - err.help(format!("{}{}", or, help_msg)); - } + ( + b.span, + note_msg, + help_msgs + .iter() + .enumerate() + .map(|(i, help_msg)| { + let or = if i == 0 { "" } else { "or " }; + format!("{or}{help_msg}") + }) + .collect::<Vec<_>>(), + ) }; - - could_refer_to(b1, misc1, ""); - could_refer_to(b2, misc2, " also"); - err.emit(); + let (b1_span, b1_note_msg, b1_help_msgs) = could_refer_to(b1, misc1, ""); + let (b2_span, b2_note_msg, b2_help_msgs) = could_refer_to(b2, misc2, " also"); + + AmbiguityErrorDiag { + msg: format!("`{ident}` is ambiguous"), + span: ident.span, + label_span: ident.span, + label_msg: "ambiguous name".to_string(), + note_msg: format!("ambiguous because of {}", kind.descr()), + b1_span, + b1_note_msg, + b1_help_msgs, + b2_span, + b2_note_msg, + b2_help_msgs, + } } /// If the binding refers to a tuple struct constructor with fields, @@ -1626,7 +1653,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let descr = get_descr(binding); let mut err = struct_span_err!(self.tcx.sess, ident.span, E0603, "{} `{}` is private", descr, ident); - err.span_label(ident.span, format!("private {}", descr)); + err.span_label(ident.span, format!("private {descr}")); if let Some((this_res, outer_ident)) = outermost_res { let import_suggestions = self.lookup_import_candidates( @@ -1718,7 +1745,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if next_binding.is_none() && let Some(span) = non_exhaustive { note_span.push_span_label( span, - format!("cannot be constructed because it is `#[non_exhaustive]`"), + "cannot be constructed because it is `#[non_exhaustive]`", ); } err.span_note(note_span, msg); @@ -1811,7 +1838,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { _ => format!("`{parent}`"), }; - let mut msg = format!("could not find `{}` in {}", ident, parent); + let mut msg = format!("could not find `{ident}` in {parent}"); if ns == TypeNS || ns == ValueNS { let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; let binding = if let Some(module) = module { @@ -1926,12 +1953,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let suggestion = match_span.map(|span| { ( vec![(span, String::from(""))], - format!("`{}` is defined here, but is not a type", ident), + format!("`{ident}` is defined here, but is not a type"), Applicability::MaybeIncorrect, ) }); - (format!("use of undeclared type `{}`", ident), suggestion) + (format!("use of undeclared type `{ident}`"), suggestion) } else { let mut suggestion = None; if ident.name == sym::alloc { @@ -1953,7 +1980,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }, ) }); - (format!("use of undeclared crate or module `{}`", ident), suggestion) + (format!("use of undeclared crate or module `{ident}`"), suggestion) } } @@ -1973,7 +2000,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {} // `ident::...` on 2018. (Some(fst), _) - if fst.ident.span.rust_2018() && !fst.ident.is_path_segment_keyword() => + if fst.ident.span.at_least_rust_2018() && !fst.ident.is_path_segment_keyword() => { // Insert a placeholder that's later replaced by `self`/`super`/etc. path.insert(0, Segment::from_ident(Ident::empty())); @@ -2137,16 +2164,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let module_name = crate_module.kind.name().unwrap(); let import_snippet = match import.kind { ImportKind::Single { source, target, .. } if source != target => { - format!("{} as {}", source, target) + format!("{source} as {target}") } - _ => format!("{}", ident), + _ => format!("{ident}"), }; let mut corrections: Vec<(Span, String)> = Vec::new(); if !import.is_nested() { // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove // intermediate segments. - corrections.push((import.span, format!("{}::{}", module_name, import_snippet))); + corrections.push((import.span, format!("{module_name}::{import_snippet}"))); } else { // Find the binding span (and any trailing commas and spaces). // ie. `use a::b::{c, d, e};` @@ -2213,11 +2240,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { start_point, if has_nested { // In this case, `start_snippet` must equal '{'. - format!("{}{}, ", start_snippet, import_snippet) + format!("{start_snippet}{import_snippet}, ") } else { // In this case, add a `{`, then the moved import, then whatever // was there before. - format!("{{{}, {}", import_snippet, start_snippet) + format!("{{{import_snippet}, {start_snippet}") }, )); @@ -2634,9 +2661,9 @@ fn show_candidates( "item" }; let plural_descr = - if descr.ends_with('s') { format!("{}es", descr) } else { format!("{}s", descr) }; + if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; - let mut msg = format!("{}these {} exist but are inaccessible", prefix, plural_descr); + let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible"); let mut has_colon = false; let mut spans = Vec::new(); @@ -2657,7 +2684,7 @@ fn show_candidates( let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); for (name, span) in spans { - multi_span.push_span_label(span, format!("`{}`: not accessible", name)); + multi_span.push_span_label(span, format!("`{name}`: not accessible")); } for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 520fab1f0c8..3bd9cea27ce 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -677,6 +677,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident: orig_ident, b1: innermost_binding, b2: binding, + warning: false, misc1: misc(innermost_flags), misc2: misc(flags), }); @@ -905,6 +906,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ident, b1: binding, b2: shadowed_glob, + warning: false, misc1: AmbiguityErrorMisc::None, misc2: AmbiguityErrorMisc::None, }); @@ -1417,13 +1419,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { )); continue; } - if name == kw::PathRoot && ident.span.rust_2018() { + if name == kw::PathRoot && ident.span.at_least_rust_2018() { module = Some(ModuleOrUniformRoot::ExternPrelude); continue; } if name == kw::PathRoot && ident.span.is_rust_2015() - && self.tcx.sess.rust_2018() + && self.tcx.sess.at_least_rust_2018() { // `::a::b` from 2015 macro on 2018 global edition module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude); @@ -1443,12 +1445,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let name_str = if name == kw::PathRoot { "crate root".to_string() } else { - format!("`{}`", name) + format!("`{name}`") }; let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { - format!("global paths cannot start with {}", name_str) + format!("global paths cannot start with {name_str}") } else { - format!("{} in paths can only be used in start position", name_str) + format!("{name_str} in paths can only be used in start position") }; (label, None) }); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 526fc9c3aa5..a175d9f6c7f 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -284,6 +284,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.arenas.alloc_name_binding(NameBindingData { kind: NameBindingKind::Import { binding, import, used: Cell::new(false) }, ambiguity: None, + warn_ambiguity: false, span: import.span, vis, expansion: import.parent_scope.expansion, @@ -291,16 +292,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } /// Define the name or return the existing binding if there is a collision. + /// `update` indicates if the definition is a redefinition of an existing binding. pub(crate) fn try_define( &mut self, module: Module<'a>, key: BindingKey, binding: NameBinding<'a>, + warn_ambiguity: bool, ) -> Result<(), NameBinding<'a>> { let res = binding.res(); self.check_reserved_macro_name(key.ident, res); self.set_binding_parent_module(binding, module); - self.update_resolution(module, key, |this, resolution| { + self.update_resolution(module, key, warn_ambiguity, |this, resolution| { if let Some(old_binding) = resolution.binding { if res == Res::Err && old_binding.res() != Res::Err { // Do not override real bindings with `Res::Err`s from error recovery. @@ -308,15 +311,42 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } match (old_binding.is_glob_import(), binding.is_glob_import()) { (true, true) => { - if res != old_binding.res() { - resolution.binding = Some(this.ambiguity( - AmbiguityKind::GlobVsGlob, - old_binding, - binding, - )); + // FIXME: remove `!binding.is_ambiguity()` after delete the warning ambiguity. + if !binding.is_ambiguity() + && let NameBindingKind::Import { import: old_import, .. } = old_binding.kind + && let NameBindingKind::Import { import, .. } = binding.kind + && old_import == import { + // We should replace the `old_binding` with `binding` regardless + // of whether they has same resolution or not when they are + // imported from the same glob-import statement. + // However we currently using `Some(old_binding)` for back compact + // purposes. + // This case can be removed after once `Undetermined` is prepared + // for glob-imports. + } else if res != old_binding.res() { + let binding = if warn_ambiguity { + this.warn_ambiguity( + AmbiguityKind::GlobVsGlob, + old_binding, + binding, + ) + } else { + this.ambiguity( + AmbiguityKind::GlobVsGlob, + old_binding, + binding, + ) + }; + resolution.binding = Some(binding); } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) { // We are glob-importing the same item but with greater visibility. resolution.binding = Some(binding); + } else if binding.is_ambiguity() { + resolution.binding = + Some(self.arenas.alloc_name_binding(NameBindingData { + warn_ambiguity: true, + ..(*binding).clone() + })); } } (old_glob @ true, false) | (old_glob @ false, true) => { @@ -374,29 +404,52 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }) } + fn warn_ambiguity( + &self, + kind: AmbiguityKind, + primary_binding: NameBinding<'a>, + secondary_binding: NameBinding<'a>, + ) -> NameBinding<'a> { + self.arenas.alloc_name_binding(NameBindingData { + ambiguity: Some((secondary_binding, kind)), + warn_ambiguity: true, + ..(*primary_binding).clone() + }) + } + // Use `f` to mutate the resolution of the name in the module. // If the resolution becomes a success, define it in the module's glob importers. - fn update_resolution<T, F>(&mut self, module: Module<'a>, key: BindingKey, f: F) -> T + fn update_resolution<T, F>( + &mut self, + module: Module<'a>, + key: BindingKey, + warn_ambiguity: bool, + f: F, + ) -> T where F: FnOnce(&mut Resolver<'a, 'tcx>, &mut NameResolution<'a>) -> T, { // Ensure that `resolution` isn't borrowed when defining in the module's glob importers, // during which the resolution might end up getting re-defined via a glob cycle. - let (binding, t) = { + let (binding, t, warn_ambiguity) = { let resolution = &mut *self.resolution(module, key).borrow_mut(); let old_binding = resolution.binding(); let t = f(self, resolution); - if old_binding.is_none() && let Some(binding) = resolution.binding() { - (binding, t) + if let Some(binding) = resolution.binding() && old_binding != Some(binding) { + (binding, t, warn_ambiguity || old_binding.is_some()) } else { return t; } }; - // Define `binding` in `module`s glob importers. - for import in module.glob_importers.borrow_mut().iter() { + let Ok(glob_importers) = module.glob_importers.try_borrow_mut() else { + return t; + }; + + // Define or update `binding` in `module`s glob importers. + for import in glob_importers.iter() { let mut ident = key.ident; let scope = match ident.span.reverse_glob_adjust(module.expansion, import.span) { Some(Some(def)) => self.expn_def_scope(def), @@ -406,7 +459,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if self.is_accessible_from(binding.vis, scope) { let imported_binding = self.import(binding, *import); let key = BindingKey { ident, ..key }; - let _ = self.try_define(import.parent_scope.module, key, imported_binding); + let _ = self.try_define( + import.parent_scope.module, + key, + imported_binding, + warn_ambiguity, + ); } } @@ -425,7 +483,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let dummy_binding = self.import(dummy_binding, import); self.per_ns(|this, ns| { let key = BindingKey::new(target, ns); - let _ = this.try_define(import.parent_scope.module, key, dummy_binding); + let _ = this.try_define(import.parent_scope.module, key, dummy_binding, false); }); self.record_use(target, dummy_binding, false); } else if import.imported_module.get().is_none() { @@ -475,15 +533,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let indeterminate_imports = mem::take(&mut self.indeterminate_imports); for (is_indeterminate, import) in determined_imports - .into_iter() + .iter() .map(|i| (false, i)) - .chain(indeterminate_imports.into_iter().map(|i| (true, i))) + .chain(indeterminate_imports.iter().map(|i| (true, i))) { - let unresolved_import_error = self.finalize_import(import); + let unresolved_import_error = self.finalize_import(*import); // If this import is unresolved then create a dummy import // resolution for it so that later resolve stages won't complain. - self.import_dummy_binding(import, is_indeterminate); + self.import_dummy_binding(*import, is_indeterminate); if let Some(err) = unresolved_import_error { if let ImportKind::Single { source, ref source_bindings, .. } = import.kind { @@ -505,27 +563,34 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { errors = vec![]; } if seen_spans.insert(err.span) { - errors.push((import, err)); + errors.push((*import, err)); prev_root_id = import.root_id; } - } else if is_indeterminate { - let path = import_path_to_string( - &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), - &import.kind, - import.span, - ); - let err = UnresolvedImportError { - span: import.span, - label: None, - note: None, - suggestion: None, - candidates: None, - }; - // FIXME: there should be a better way of doing this than - // formatting this as a string then checking for `::` - if path.contains("::") { - errors.push((import, err)) - } + } + } + + if !errors.is_empty() { + self.throw_unresolved_import_error(errors); + return; + } + + for import in &indeterminate_imports { + let path = import_path_to_string( + &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), + &import.kind, + import.span, + ); + let err = UnresolvedImportError { + span: import.span, + label: None, + note: None, + suggestion: None, + candidates: None, + }; + // FIXME: there should be a better way of doing this than + // formatting this as a string then checking for `::` + if path.contains("::") { + errors.push((*import, err)) } } @@ -700,7 +765,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Segment::names_to_string(&import.module_path), module_to_string(import.parent_scope.module).unwrap_or_else(|| "???".to_string()), ); - let module = if let Some(module) = import.imported_module.get() { module } else { @@ -773,7 +837,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .emit(); } let key = BindingKey::new(target, ns); - this.update_resolution(parent, key, |_, resolution| { + this.update_resolution(parent, key, false, |_, resolution| { resolution.single_imports.remove(&import); }); } @@ -989,14 +1053,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { initial_binding.res() }); let res = binding.res(); + let has_ambiguity_error = this + .ambiguity_errors + .iter() + .filter(|error| !error.warning) + .next() + .is_some(); + if res == Res::Err || has_ambiguity_error { + this.tcx + .sess + .delay_span_bug(import.span, "some error happened for an import"); + return; + } if let Ok(initial_res) = initial_res { - if res != initial_res && this.ambiguity_errors.is_empty() { + if res != initial_res { span_bug!(import.span, "inconsistent resolution for an import"); } - } else if res != Res::Err - && this.ambiguity_errors.is_empty() - && this.privacy_errors.is_empty() - { + } else if this.privacy_errors.is_empty() { this.tcx .sess .create_err(CannotDetermineImportResolution { span: import.span }) @@ -1087,18 +1160,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ModuleOrUniformRoot::Module(module) => { let module_str = module_to_string(module); if let Some(module_str) = module_str { - format!("no `{}` in `{}`", ident, module_str) + format!("no `{ident}` in `{module_str}`") } else { - format!("no `{}` in the root", ident) + format!("no `{ident}` in the root") } } _ => { if !ident.is_path_segment_keyword() { - format!("no external crate `{}`", ident) + format!("no external crate `{ident}`") } else { // HACK(eddyb) this shows up for `self` & `super`, which // should work instead - for now keep the same error message. - format!("no `{}` in the root", ident) + format!("no `{ident}` in the root") } } }; @@ -1146,10 +1219,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let (ns, binding) = reexport_error.unwrap(); if pub_use_of_private_extern_crate_hack(import, binding) { let msg = format!( - "extern crate `{}` is private, and cannot be \ + "extern crate `{ident}` is private, and cannot be \ re-exported (error E0365), consider declaring with \ - `pub`", - ident + `pub`" ); self.lint_buffer.buffer_lint( PUB_USE_OF_PRIVATE_EXTERN_CRATE, @@ -1289,7 +1361,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { UNUSED_IMPORTS, id, import.span, - format!("the item `{}` is imported redundantly", ident), + format!("the item `{ident}` is imported redundantly"), BuiltinLintDiagnostics::RedundantImport(redundant_spans, ident), ); } @@ -1335,7 +1407,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; if self.is_accessible_from(binding.vis, scope) { let imported_binding = self.import(binding, import); - let _ = self.try_define(import.parent_scope.module, key, imported_binding); + let warn_ambiguity = self + .resolution(import.parent_scope.module, key) + .borrow() + .binding() + .is_some_and(|binding| binding.is_warn_ambiguity()); + let _ = self.try_define( + import.parent_scope.module, + key, + imported_binding, + warn_ambiguity, + ); } } @@ -1354,7 +1436,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module.for_each_child(self, |this, ident, _, binding| { let res = binding.res().expect_non_local(); - if res != def::Res::Err && !binding.is_ambiguity() { + let error_ambiguity = binding.is_ambiguity() && !binding.warn_ambiguity; + if res != def::Res::Err && !error_ambiguity { let mut reexport_chain = SmallVec::new(); let mut next_binding = binding; while let NameBindingKind::Import { binding, import, .. } = next_binding.kind { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 846a1ffe09b..0e9d74480a9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -337,6 +337,7 @@ enum LifetimeBinderKind { PolyTrait, WhereBound, Item, + ConstItem, Function, Closure, ImplBlock, @@ -349,7 +350,7 @@ impl LifetimeBinderKind { BareFnType => "type", PolyTrait => "bound", WhereBound => "bound", - Item => "item", + Item | ConstItem => "item", ImplBlock => "impl block", Function => "function", Closure => "closure", @@ -549,6 +550,7 @@ enum MaybeExported<'a> { Ok(NodeId), Impl(Option<DefId>), ImplItem(Result<DefId, &'a Visibility>), + NestedUse(&'a Visibility), } impl MaybeExported<'_> { @@ -559,7 +561,9 @@ impl MaybeExported<'_> { trait_def_id.as_local() } MaybeExported::Impl(None) => return true, - MaybeExported::ImplItem(Err(vis)) => return vis.kind.is_pub(), + MaybeExported::ImplItem(Err(vis)) | MaybeExported::NestedUse(vis) => { + return vis.kind.is_pub(); + } }; def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id)) } @@ -1913,10 +1917,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { candidate: LifetimeElisionCandidate, ) { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { - panic!( - "lifetime {:?} resolved multiple times ({:?} before, {:?} now)", - id, prev_res, res - ) + panic!("lifetime {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)") } match res { LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => { @@ -1932,8 +1933,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) { if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) { panic!( - "lifetime parameter {:?} resolved multiple times ({:?} before, {:?} now)", - id, prev_res, res + "lifetime parameter {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)" ) } } @@ -2284,7 +2284,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { fn resolve_item(&mut self, item: &'ast Item) { let mod_inner_docs = matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs); - if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..)) { + if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..) | ItemKind::Use(..)) { self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); } @@ -2401,33 +2401,53 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { }); } - ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) - | ItemKind::Const(box ast::ConstItem { ref ty, ref expr, .. }) => { + ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) => { self.with_static_rib(|this| { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); }); - this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(expr) = expr { + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.resolve_const_body(expr, Some((item.ident, ConstantItemKind::Static))); + } + }); + } + + ItemKind::Const(box ast::ConstItem { ref generics, ref ty, ref expr, .. }) => { + self.with_generic_param_rib( + &generics.params, + RibKind::Item(HasGenericParams::Yes(generics.span)), + LifetimeRibKind::Generics { + binder: item.id, + kind: LifetimeBinderKind::ConstItem, + span: generics.span, + }, + |this| { + this.visit_generics(generics); + + this.with_lifetime_rib( + LifetimeRibKind::Elided(LifetimeRes::Static), + |this| this.visit_ty(ty), + ); + if let Some(expr) = expr { - let constant_item_kind = match item.kind { - ItemKind::Const(..) => ConstantItemKind::Const, - ItemKind::Static(..) => ConstantItemKind::Static, - _ => unreachable!(), - }; - // We already forbid generic params because of the above item rib, - // so it doesn't matter whether this is a trivial constant. - this.with_constant_rib( - IsRepeatExpr::No, - ConstantHasGenerics::Yes, - Some((item.ident, constant_item_kind)), - |this| this.visit_expr(expr), + this.resolve_const_body( + expr, + Some((item.ident, ConstantItemKind::Const)), ); } - }); - }); + }, + ); } ItemKind::Use(ref use_tree) => { + let maybe_exported = match use_tree.kind { + UseTreeKind::Simple(_) | UseTreeKind::Glob => MaybeExported::Ok(item.id), + UseTreeKind::Nested(_) => MaybeExported::NestedUse(&item.vis), + }; + self.resolve_doc_links(&item.attrs, maybe_exported); + self.future_proof_import(use_tree); } @@ -2569,7 +2589,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let res = match kind { RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()), RibKind::Normal => { - if self.r.tcx.sess.features_untracked().non_lifetime_binders { + if self.r.tcx.features().non_lifetime_binders { Res::Def(def_kind, def_id.to_def_id()) } else { Res::Err @@ -2691,28 +2711,31 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { for item in trait_items { self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); match &item.kind { - AssocItemKind::Const(box ast::ConstItem { ty, expr, .. }) => { - self.visit_ty(ty); - // Only impose the restrictions of `ConstRibKind` for an - // actual constant expression in a provided default. - if let Some(expr) = expr { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - self.with_lifetime_rib( - LifetimeRibKind::Elided(LifetimeRes::Infer), - |this| { - this.with_constant_rib( - IsRepeatExpr::No, - ConstantHasGenerics::Yes, - None, - |this| this.visit_expr(expr), - ) - }, - ); - } + AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { + self.with_generic_param_rib( + &generics.params, + RibKind::AssocItem, + LifetimeRibKind::Generics { + binder: item.id, + span: generics.span, + kind: LifetimeBinderKind::ConstItem, + }, + |this| { + this.visit_generics(generics); + this.visit_ty(ty); + + // Only impose the restrictions of `ConstRibKind` for an + // actual constant expression in a provided default. + if let Some(expr) = expr { + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_body(expr, None); + } + }, + ); } AssocItemKind::Fn(box Fn { generics, .. }) => { walk_assoc_item(self, generics, LifetimeBinderKind::Function, item); @@ -2867,36 +2890,42 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { use crate::ResolutionError::*; self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis))); match &item.kind { - AssocItemKind::Const(box ast::ConstItem { ty, expr, .. }) => { + AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => { debug!("resolve_implementation AssocItemKind::Const"); - // If this is a trait impl, ensure the const - // exists in trait - self.check_trait_item( - item.id, - item.ident, - &item.kind, - ValueNS, - item.span, - seen_trait_items, - |i, s, c| ConstNotMemberOfTrait(i, s, c), - ); - self.visit_ty(ty); - if let Some(expr) = expr { - // We allow arbitrary const expressions inside of associated consts, - // even if they are potentially not const evaluatable. - // - // Type parameters can already be used and as associated consts are - // not used as part of the type system, this is far less surprising. - self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { - this.with_constant_rib( - IsRepeatExpr::No, - ConstantHasGenerics::Yes, - None, - |this| this.visit_expr(expr), - ) - }); - } + self.with_generic_param_rib( + &generics.params, + RibKind::AssocItem, + LifetimeRibKind::Generics { + binder: item.id, + span: generics.span, + kind: LifetimeBinderKind::ConstItem, + }, + |this| { + // If this is a trait impl, ensure the const + // exists in trait + this.check_trait_item( + item.id, + item.ident, + &item.kind, + ValueNS, + item.span, + seen_trait_items, + |i, s, c| ConstNotMemberOfTrait(i, s, c), + ); + + this.visit_generics(generics); + this.visit_ty(ty); + if let Some(expr) = expr { + // We allow arbitrary const expressions inside of associated consts, + // even if they are potentially not const evaluatable. + // + // Type parameters can already be used and as associated consts are + // not used as part of the type system, this is far less surprising. + this.resolve_const_body(expr, None); + } + }, + ); } AssocItemKind::Fn(box Fn { generics, .. }) => { debug!("resolve_implementation AssocItemKind::Fn"); @@ -3054,6 +3083,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ); } + fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) { + self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| { + this.visit_expr(expr) + }); + }) + } + fn resolve_params(&mut self, params: &'ast [Param]) { let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { @@ -3911,8 +3948,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { && path[0].ident.name != kw::PathRoot && path[0].ident.name != kw::DollarCrate { + let last_segment = *path.last().unwrap(); let unqualified_result = { - match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) { + match self.resolve_path(&[last_segment], Some(ns), None) { PathResult::NonModule(path_res) => path_res.expect_full_res(), PathResult::Module(ModuleOrUniformRoot::Module(module)) => { module.res().unwrap() @@ -3928,8 +3966,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { finalize.path_span, "unnecessary qualification", lint::BuiltinLintDiagnostics::UnusedQualifications { - path_span: finalize.path_span, - unqualified_path: path.last().unwrap().ident + removal_span: finalize.path_span.until(last_segment.ident.span), } ) } @@ -4439,6 +4476,7 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { fn visit_item(&mut self, item: &'ast Item) { match &item.kind { ItemKind::TyAlias(box TyAlias { ref generics, .. }) + | ItemKind::Const(box ConstItem { ref generics, .. }) | ItemKind::Fn(box Fn { ref generics, .. }) | ItemKind::Enum(_, ref generics) | ItemKind::Struct(_, ref generics) @@ -4458,7 +4496,6 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> { ItemKind::Mod(..) | ItemKind::ForeignMod(..) | ItemKind::Static(..) - | ItemKind::Const(..) | ItemKind::Use(..) | ItemKind::ExternCrate(..) | ItemKind::MacroDef(..) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 753a1adc66d..974580f815b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -585,13 +585,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let others = match enum_candidates.len() { 1 => String::new(), 2 => " and 1 other".to_owned(), - n => format!(" and {} others", n), + n => format!(" and {n} others"), }; format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others) } else { String::new() }; - let msg = format!("{}try using the variant's enum", preamble); + let msg = format!("{preamble}try using the variant's enum"); err.span_suggestions( span, @@ -696,7 +696,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ident.name == path[0].ident.name { err.span_help( ident.span, - format!("the binding `{}` is available in a different scope in the same function", path_str), + format!("the binding `{path_str}` is available in a different scope in the same function"), ); return (true, candidates); } @@ -858,7 +858,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { for label_rib in &self.label_ribs { for (label_ident, node_id) in &label_rib.bindings { let ident = path.last().unwrap().ident; - if format!("'{}", ident) == label_ident.to_string() { + if format!("'{ident}") == label_ident.to_string() { err.span_label(label_ident.span, "a label with a similar name exists"); if let PathSource::Expr(Some(Expr { kind: ExprKind::Break(None, Some(_)), @@ -983,7 +983,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if let Some(ident) = fn_kind.ident() { err.span_label( ident.span, - format!("this function {} have a `self` parameter", doesnt), + format!("this function {doesnt} have a `self` parameter"), ); } } @@ -1164,7 +1164,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }; err.span_suggestion_verbose( *where_span, - format!("constrain the associated type to `{}`", ident), + format!("constrain the associated type to `{ident}`"), where_bound_predicate_to_string(&new_where_bound_predicate), Applicability::MaybeIncorrect, ); @@ -1338,8 +1338,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { span, // Note the parentheses surrounding the suggestion below format!( "you might want to surround a struct literal with parentheses: \ - `({} {{ /* fields */ }})`?", - path_str + `({path_str} {{ /* fields */ }})`?" ), ); } @@ -1373,7 +1372,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .map(|(idx, new)| (new, old_fields.get(idx))) .map(|(new, old)| { let new = new.to_ident_string(); - if let Some(Some(old)) = old && new != *old { format!("{}: {}", new, old) } else { new } + if let Some(Some(old)) = old && new != *old { format!("{new}: {old}") } else { new } }) .collect::<Vec<String>>() } else { @@ -1390,7 +1389,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }; err.span_suggestion( span, - format!("use struct {} syntax instead", descr), + format!("use struct {descr} syntax instead"), format!("{path_str} {{{pad}{fields}{pad}}}"), applicability, ); @@ -1584,7 +1583,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { err.span_suggestion( span, "use the tuple variant pattern syntax instead", - format!("{}({})", path_str, fields), + format!("{path_str}({fields})"), Applicability::HasPlaceholders, ); } @@ -1994,9 +1993,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if !suggestable_variants.is_empty() { let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 { - format!("try {} the enum's variant", source_msg) + format!("try {source_msg} the enum's variant") } else { - format!("try {} one of the enum's variants", source_msg) + format!("try {source_msg} one of the enum's variants") }; err.span_suggestions( @@ -2009,19 +2008,15 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { // If the enum has no tuple variants.. if non_suggestable_variant_count == variants.len() { - err.help(format!("the enum has no tuple variants {}", source_msg)); + err.help(format!("the enum has no tuple variants {source_msg}")); } // If there are also non-tuple variants.. if non_suggestable_variant_count == 1 { - err.help(format!( - "you might have meant {} the enum's non-tuple variant", - source_msg - )); + err.help(format!("you might have meant {source_msg} the enum's non-tuple variant")); } else if non_suggestable_variant_count >= 1 { err.help(format!( - "you might have meant {} one of the enum's non-tuple variants", - source_msg + "you might have meant {source_msg} one of the enum's non-tuple variants" )); } } else { @@ -2041,7 +2036,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) .map(|(variant, kind)| match kind { CtorKind::Const => variant, - CtorKind::Fn => format!("({}())", variant), + CtorKind::Fn => format!("({variant}())"), }) .collect::<Vec<_>>(); let no_suggestable_variant = suggestable_variants.is_empty(); @@ -2066,7 +2061,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { .filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind)) .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) .filter_map(|(variant, kind)| match kind { - CtorKind::Fn => Some(format!("({}(/* fields */))", variant)), + CtorKind::Fn => Some(format!("({variant}(/* fields */))")), _ => None, }) .collect::<Vec<_>>(); @@ -2348,13 +2343,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { let mut should_continue = true; match rib.kind { LifetimeRibKind::Generics { binder: _, span, kind } => { + // Avoid suggesting placing lifetime parameters on constant items unless the relevant + // feature is enabled. Suggest the parent item as a possible location if applicable. + if let LifetimeBinderKind::ConstItem = kind + && !self.r.tcx().features().generic_const_items + { + continue; + } + if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( span, format!( - "lifetime `{}` is missing in item created through this procedural macro", - name, + "lifetime `{name}` is missing in item created through this procedural macro", ), ); continue; @@ -2398,7 +2400,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ); } else if let Some(name) = name { let message = - Cow::from(format!("consider introducing lifetime `{}` here", name)); + Cow::from(format!("consider introducing lifetime `{name}` here")); should_continue = suggest(err, false, span, message, sugg); } else { let message = Cow::from("consider introducing a named lifetime parameter"); @@ -2542,7 +2544,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } let help_name = if let Some(ident) = ident { - format!("`{}`", ident) + format!("`{ident}`") } else { format!("argument {}", index + 1) }; @@ -2550,7 +2552,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if lifetime_count == 1 { m.push_str(&help_name[..]) } else { - m.push_str(&format!("one of {}'s {} lifetimes", help_name, lifetime_count)[..]) + m.push_str(&format!("one of {help_name}'s {lifetime_count} lifetimes")[..]) } } @@ -2580,14 +2582,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else if num_params == 1 { err.help(format!( "this function's return type contains a borrowed value, \ - but the signature does not say which {} it is borrowed from", - m + but the signature does not say which {m} it is borrowed from" )); } else { err.help(format!( "this function's return type contains a borrowed value, \ - but the signature does not say whether it is borrowed from {}", - m + but the signature does not say whether it is borrowed from {m}" )); } } @@ -2606,7 +2606,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } MissingLifetimeKind::Ampersand => { debug_assert_eq!(lt.count, 1); - (lt.span.shrink_to_hi(), format!("{} ", existing_name)) + (lt.span.shrink_to_hi(), format!("{existing_name} ")) } MissingLifetimeKind::Comma => { let sugg: String = std::iter::repeat([existing_name.as_str(), ", "]) @@ -2653,7 +2653,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } 1 => { err.multipart_suggestion_verbose( - format!("consider using the `{}` lifetime", existing_name), + format!("consider using the `{existing_name}` lifetime"), spans_suggs, Applicability::MaybeIncorrect, ); @@ -2770,9 +2770,9 @@ pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident let shadower = shadower.span; let mut err = sess.struct_span_warn( shadower, - format!("label name `{}` shadows a label name that is already in scope", name), + format!("label name `{name}` shadows a label name that is already in scope"), ); err.span_label(orig, "first declared here"); - err.span_label(shadower, format!("label `{}` already in scope", name)); + err.span_label(shadower, format!("label `{name}` already in scope")); err.emit(); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index faa672db59c..da5e92a075a 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -658,6 +658,7 @@ impl<'a> fmt::Debug for Module<'a> { struct NameBindingData<'a> { kind: NameBindingKind<'a>, ambiguity: Option<(NameBinding<'a>, AmbiguityKind)>, + warn_ambiguity: bool, expansion: LocalExpnId, span: Span, vis: ty::Visibility<DefId>, @@ -767,6 +768,7 @@ struct AmbiguityError<'a> { b2: NameBinding<'a>, misc1: AmbiguityErrorMisc, misc2: AmbiguityErrorMisc, + warning: bool, } impl<'a> NameBindingData<'a> { @@ -794,6 +796,14 @@ impl<'a> NameBindingData<'a> { } } + fn is_warn_ambiguity(&self) -> bool { + self.warn_ambiguity + || match self.kind { + NameBindingKind::Import { binding, .. } => binding.is_warn_ambiguity(), + _ => false, + } + } + fn is_possibly_imported_variant(&self) -> bool { match self.kind { NameBindingKind::Import { binding, .. } => binding.is_possibly_imported_variant(), @@ -1158,7 +1168,7 @@ impl<'tcx> Resolver<'_, 'tcx> { } fn local_def_id(&self, node: NodeId) -> LocalDefId { - self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node)) + self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) } /// Adds a definition with a parent definition. @@ -1322,6 +1332,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { dummy_binding: arenas.alloc_name_binding(NameBindingData { kind: NameBindingKind::Res(Res::Err), ambiguity: None, + warn_ambiguity: false, expansion: LocalExpnId::ROOT, span: DUMMY_SP, vis: ty::Visibility::Public, @@ -1685,6 +1696,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } fn record_use(&mut self, ident: Ident, used_binding: NameBinding<'a>, is_lexical_scope: bool) { + self.record_use_inner(ident, used_binding, is_lexical_scope, used_binding.warn_ambiguity); + } + + fn record_use_inner( + &mut self, + ident: Ident, + used_binding: NameBinding<'a>, + is_lexical_scope: bool, + warn_ambiguity: bool, + ) { if let Some((b2, kind)) = used_binding.ambiguity { let ambiguity_error = AmbiguityError { kind, @@ -1693,9 +1714,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { b2, misc1: AmbiguityErrorMisc::None, misc2: AmbiguityErrorMisc::None, + warning: warn_ambiguity, }; if !self.matches_previous_ambiguity_error(&ambiguity_error) { - // avoid duplicated span information to be emitt out + // avoid duplicated span information to be emit out self.ambiguity_errors.push(ambiguity_error); } } @@ -1715,7 +1737,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.used_imports.insert(id); } self.add_to_glob_map(import, ident); - self.record_use(ident, binding, false); + self.record_use_inner(ident, binding, false, warn_ambiguity || binding.warn_ambiguity); } } @@ -1812,7 +1834,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { fn record_partial_res(&mut self, node_id: NodeId, resolution: PartialRes) { debug!("(recording res) recording {:?} for {}", resolution, node_id); if let Some(prev_res) = self.partial_res_map.insert(node_id, resolution) { - panic!("path resolved multiple times ({:?} before, {:?} now)", prev_res, resolution); + panic!("path resolved multiple times ({prev_res:?} before, {resolution:?} now)"); } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d16b7902f60..614a29e7578 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -24,7 +24,9 @@ use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; use rustc_middle::ty::TyCtxt; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE}; +use rustc_session::lint::builtin::{ + LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, +}; use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; @@ -140,9 +142,9 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { } } } - // We implicitly add `rustfmt` and `clippy` to known tools, + // We implicitly add `rustfmt`, `clippy`, `diagnostic` to known tools, // but it's not an error to register them explicitly. - let predefined_tools = [sym::clippy, sym::rustfmt]; + let predefined_tools = [sym::clippy, sym::rustfmt, sym::diagnostic]; registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span)); registered_tools } @@ -205,7 +207,7 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { self.tcx .sess .diagnostic() - .bug(format!("built-in macro `{}` was already registered", name)); + .bug(format!("built-in macro `{name}` was already registered")); } } @@ -568,7 +570,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let mut err = self.tcx.sess.create_err(err); - err.span_label(path.span, format!("not {} {}", article, expected)); + err.span_label(path.span, format!("not {article} {expected}")); err.emit(); @@ -576,10 +578,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } // We are trying to avoid reporting this error if other related errors were reported. - if res != Res::Err - && inner_attr - && !self.tcx.sess.features_untracked().custom_inner_attributes - { + if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes { let msg = match res { Res::Def(..) => "inner macro attributes are unstable", Res::NonMacroAttr(..) => "custom inner attributes are unstable", @@ -598,6 +597,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } + if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) + && path.segments.len() >= 2 + && path.segments[0].ident.name == sym::diagnostic + { + self.tcx.sess.parse_sess.buffer_lint( + UNKNOWN_DIAGNOSTIC_ATTRIBUTES, + path.segments[1].span(), + node_id, + "unknown diagnostic attribute", + ); + } + Ok((ext, res)) } @@ -895,7 +906,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if macro_kind.is_some() && sub_namespace_match(macro_kind, Some(MacroKind::Attr)) { self.tcx.sess.span_err( ident.span, - format!("name `{}` is reserved in attribute namespace", ident), + format!("name `{ident}` is reserved in attribute namespace"), ); } } diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 1291d1454a6..e26d25d9a41 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [dependencies] -atty = "0.2.13" bitflags = "1.2.1" getopts = "0.2" rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 4897bd8d5da..d4042a2e61a 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -26,6 +26,8 @@ session_feature_gate_error = {$explain} session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions +session_file_write_fail = failed to write `{$path}` due to error `{$err}` + session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target @@ -43,6 +45,7 @@ session_int_literal_too_large = integer literal is too large .note = value exceeds limit of `{$limit}` session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}` +session_invalid_character_in_create_name_help = you can either pass `--crate-name` on the command line or add `#![crate_name="…"]` to set the crate name session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal .label = invalid suffix `{$suffix}` diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index cabe1c96bf7..df81e1f8305 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -227,10 +227,8 @@ impl CodeStats { } pub fn print_vtable_sizes(&self, crate_name: &str) { - let mut infos = std::mem::take(&mut *self.vtable_sizes.lock()) - .into_iter() - .map(|(_did, stats)| stats) - .collect::<Vec<_>>(); + let mut infos = + std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>(); // Primary sort: cost % in reverse order (from largest to smallest) // Secondary sort: trait_name diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 593983f117d..28ae88424ab 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3,6 +3,7 @@ pub use crate::options::*; +use crate::errors::FileWriteFail; use crate::search_paths::SearchPath; use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; use crate::{lint, HashStableContext}; @@ -31,6 +32,7 @@ use std::collections::btree_map::{ use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsStr; use std::fmt; +use std::fs; use std::hash::Hash; use std::iter; use std::path::{Path, PathBuf}; @@ -277,11 +279,11 @@ impl LinkSelfContained { // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been // set in bulk with its historical values, then manually setting a component clears that // `explicitly_set` state. - if let Some(component_to_enable) = component.strip_prefix("+") { + if let Some(component_to_enable) = component.strip_prefix('+') { self.explicitly_set = None; self.components.insert(component_to_enable.parse()?); Ok(()) - } else if let Some(component_to_disable) = component.strip_prefix("-") { + } else if let Some(component_to_disable) = component.strip_prefix('-') { self.explicitly_set = None; self.components.remove(component_to_disable.parse()?); Ok(()) @@ -710,8 +712,14 @@ impl ExternEntry { } } +#[derive(Clone, PartialEq, Debug)] +pub struct PrintRequest { + pub kind: PrintKind, + pub out: OutFileName, +} + #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum PrintRequest { +pub enum PrintKind { FileNames, Sysroot, TargetLibdir, @@ -826,9 +834,10 @@ impl OutFileName { } pub fn is_tty(&self) -> bool { + use std::io::IsTerminal; match *self { OutFileName::Real(_) => false, - OutFileName::Stdout => atty::is(atty::Stream::Stdout), + OutFileName::Stdout => std::io::stdout().is_terminal(), } } @@ -855,6 +864,17 @@ impl OutFileName { OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name), } } + + pub fn overwrite(&self, content: &str, sess: &Session) { + match self { + OutFileName::Stdout => print!("{content}"), + OutFileName::Real(path) => { + if let Err(e) = fs::write(path, content) { + sess.emit_fatal(FileWriteFail { path, err: e.to_string() }); + } + } + } + } } #[derive(Clone, Hash, Debug, HashStable_Generic)] @@ -1519,9 +1539,8 @@ pub(super) fn build_target_config( ); let (target, target_warnings) = target_result.unwrap_or_else(|e| { handler.early_error(format!( - "Error loading target specification: {}. \ - Run `rustc --print target-list` for a list of built-in targets", - e + "Error loading target specification: {e}. \ + Run `rustc --print target-list` for a list of built-in targets" )) }); for warning in target_warnings.warning_messages() { @@ -1958,8 +1977,7 @@ pub fn parse_crate_edition(handler: &EarlyErrorHandler, matches: &getopts::Match let is_nightly = nightly_options::match_is_nightly_build(matches); let msg = if !is_nightly { format!( - "the crate requires edition {}, but the latest edition supported by this Rust version is {}", - edition, LATEST_STABLE_EDITION + "the crate requires edition {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}" ) } else { format!("edition {edition} is unstable and only available with -Z unstable-options") @@ -2005,13 +2023,7 @@ fn parse_output_types( if !unstable_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { - let (shorthand, path) = match output_type.split_once('=') { - None => (output_type, None), - Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)), - Some((shorthand, path)) => { - (shorthand, Some(OutFileName::Real(PathBuf::from(path)))) - } - }; + let (shorthand, path) = split_out_file_name(output_type); let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { handler.early_error(format!( "unknown emission type: `{shorthand}` - expected one of: {display}", @@ -2028,6 +2040,14 @@ fn parse_output_types( OutputTypes(output_types) } +fn split_out_file_name(arg: &str) -> (&str, Option<OutFileName>) { + match arg.split_once('=') { + None => (arg, None), + Some((kind, "-")) => (kind, Some(OutFileName::Stdout)), + Some((kind, path)) => (kind, Some(OutFileName::Real(PathBuf::from(path)))), + } +} + fn should_override_cgus_and_disable_thinlto( handler: &EarlyErrorHandler, output_types: &OutputTypes, @@ -2091,41 +2111,55 @@ fn collect_print_requests( ) -> Vec<PrintRequest> { let mut prints = Vec::<PrintRequest>::new(); if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { - prints.push(PrintRequest::TargetCPUs); + prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); cg.target_cpu = None; }; if cg.target_feature == "help" { - prints.push(PrintRequest::TargetFeatures); + prints.push(PrintRequest { kind: PrintKind::TargetFeatures, out: OutFileName::Stdout }); cg.target_feature = String::new(); } - const PRINT_REQUESTS: &[(&str, PrintRequest)] = &[ - ("crate-name", PrintRequest::CrateName), - ("file-names", PrintRequest::FileNames), - ("sysroot", PrintRequest::Sysroot), - ("target-libdir", PrintRequest::TargetLibdir), - ("cfg", PrintRequest::Cfg), - ("calling-conventions", PrintRequest::CallingConventions), - ("target-list", PrintRequest::TargetList), - ("target-cpus", PrintRequest::TargetCPUs), - ("target-features", PrintRequest::TargetFeatures), - ("relocation-models", PrintRequest::RelocationModels), - ("code-models", PrintRequest::CodeModels), - ("tls-models", PrintRequest::TlsModels), - ("native-static-libs", PrintRequest::NativeStaticLibs), - ("stack-protector-strategies", PrintRequest::StackProtectorStrategies), - ("target-spec-json", PrintRequest::TargetSpec), - ("all-target-specs-json", PrintRequest::AllTargetSpecs), - ("link-args", PrintRequest::LinkArgs), - ("split-debuginfo", PrintRequest::SplitDebuginfo), - ("deployment-target", PrintRequest::DeploymentTarget), + const PRINT_KINDS: &[(&str, PrintKind)] = &[ + ("crate-name", PrintKind::CrateName), + ("file-names", PrintKind::FileNames), + ("sysroot", PrintKind::Sysroot), + ("target-libdir", PrintKind::TargetLibdir), + ("cfg", PrintKind::Cfg), + ("calling-conventions", PrintKind::CallingConventions), + ("target-list", PrintKind::TargetList), + ("target-cpus", PrintKind::TargetCPUs), + ("target-features", PrintKind::TargetFeatures), + ("relocation-models", PrintKind::RelocationModels), + ("code-models", PrintKind::CodeModels), + ("tls-models", PrintKind::TlsModels), + ("native-static-libs", PrintKind::NativeStaticLibs), + ("stack-protector-strategies", PrintKind::StackProtectorStrategies), + ("target-spec-json", PrintKind::TargetSpec), + ("all-target-specs-json", PrintKind::AllTargetSpecs), + ("link-args", PrintKind::LinkArgs), + ("split-debuginfo", PrintKind::SplitDebuginfo), + ("deployment-target", PrintKind::DeploymentTarget), ]; + // We disallow reusing the same path in multiple prints, such as `--print + // cfg=output.txt --print link-args=output.txt`, because outputs are printed + // by disparate pieces of the compiler, and keeping track of which files + // need to be overwritten vs appended to is annoying. + let mut printed_paths = FxHashSet::default(); + prints.extend(matches.opt_strs("print").into_iter().map(|req| { - match PRINT_REQUESTS.iter().find(|&&(name, _)| name == req) { - Some((_, PrintRequest::TargetSpec)) => { + let (req, out) = split_out_file_name(&req); + + if out.is_some() && !unstable_opts.unstable_options { + handler.early_error( + "the `-Z unstable-options` flag must also be passed to \ + enable the path print option", + ); + } + let kind = match PRINT_KINDS.iter().find(|&&(name, _)| name == req) { + Some((_, PrintKind::TargetSpec)) => { if unstable_opts.unstable_options { - PrintRequest::TargetSpec + PrintKind::TargetSpec } else { handler.early_error( "the `-Z unstable-options` flag must also be passed to \ @@ -2133,9 +2167,9 @@ fn collect_print_requests( ); } } - Some((_, PrintRequest::AllTargetSpecs)) => { + Some((_, PrintKind::AllTargetSpecs)) => { if unstable_opts.unstable_options { - PrintRequest::AllTargetSpecs + PrintKind::AllTargetSpecs } else { handler.early_error( "the `-Z unstable-options` flag must also be passed to \ @@ -2143,16 +2177,28 @@ fn collect_print_requests( ); } } - Some(&(_, print_request)) => print_request, + Some(&(_, print_kind)) => print_kind, None => { let prints = - PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>(); + PRINT_KINDS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>(); let prints = prints.join(", "); handler.early_error(format!( "unknown print request `{req}`. Valid print requests are: {prints}" )); } + }; + + let out = out.unwrap_or(OutFileName::Stdout); + if let OutFileName::Real(path) = &out { + if !printed_paths.insert(path.clone()) { + handler.early_error(format!( + "cannot print multiple outputs to the same path: {}", + path.display(), + )); + } } + + PrintRequest { kind, out } })); prints diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index dc475e8c6d5..c53a355b533 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -13,6 +13,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash, Definitions}; use rustc_span::hygiene::{ExpnHash, ExpnId}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_target::spec::abi::Abi; use rustc_target::spec::Target; use std::any::Any; @@ -147,6 +148,7 @@ pub enum DllCallingConvention { pub struct ForeignModule { pub foreign_items: Vec<DefId>, pub def_id: DefId, + pub abi: Abi, } #[derive(Copy, Clone, Debug, HashStable_Generic)] diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 4a3e668da11..1ffee01b2f1 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -164,6 +164,13 @@ pub struct FileIsNotWriteable<'a> { } #[derive(Diagnostic)] +#[diag(session_file_write_fail)] +pub(crate) struct FileWriteFail<'a> { + pub path: &'a std::path::Path, + pub err: String, +} + +#[derive(Diagnostic)] #[diag(session_crate_name_does_not_match)] pub struct CrateNameDoesNotMatch { #[primary_span] @@ -192,6 +199,14 @@ pub struct InvalidCharacterInCrateName { pub span: Option<Span>, pub character: char, pub crate_name: Symbol, + #[subdiagnostic] + pub crate_name_help: Option<InvalidCrateNameHelp>, +} + +#[derive(Subdiagnostic)] +pub enum InvalidCrateNameHelp { + #[help(session_invalid_character_in_create_name_help)] + AddCrateName, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 87d67c099ce..055ab2d9c15 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -330,8 +330,7 @@ fn build_options<O: Default>( match value { None => handler.early_error( format!( - "{0} option `{1}` requires {2} ({3} {1}=<value>)", - outputname, key, type_desc, prefix + "{outputname} option `{key}` requires {type_desc} ({prefix} {key}=<value>)" ), ), Some(value) => handler.early_error( @@ -1145,7 +1144,7 @@ mod parse { } // 2. Parse a list of enabled and disabled components. - for comp in s.split(",") { + for comp in s.split(',') { if slot.handle_cli_component(comp).is_err() { return false; } @@ -1433,8 +1432,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<usize> = (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)"), @@ -1668,6 +1665,9 @@ options! { "use a more precise version of drop elaboration for matches on enums (default: yes). \ This results in better codegen, but has caused miscompilations on some tier 2 platforms. \ See #77382 and #74551."), + #[rustc_lint_opt_deny_field_access("use `Session::print_codegen_stats` instead of this field")] + print_codegen_stats: bool = (false, parse_bool, [UNTRACKED], + "print codegen statistics (default: no)"), print_fuel: Option<String> = (None, parse_opt_string, [TRACKED], "make rustc print the total optimization fuel used by a crate"), print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], @@ -1875,10 +1875,13 @@ written to standard error output)"), Requires `-Clto[=[fat,yes]]`"), wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED], "whether to build a wasi command or reactor"), + write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], + "whether long type names should be written to files instead of being printed in errors"), // tidy-alphabetical-end // If you add a new option, please update: // - compiler/rustc_interface/src/tests.rs + // - src/doc/unstable-book/src/compiler-flags } #[derive(Clone, Hash, PartialEq, Eq, Debug)] diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 2088744bc5b..c0884fb21cd 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -2,7 +2,7 @@ use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, - InvalidCharacterInCrateName, + InvalidCharacterInCrateName, InvalidCrateNameHelp, }; use crate::Session; use rustc_ast::{self as ast, attr}; @@ -101,7 +101,16 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) { continue; } err_count += 1; - sess.emit_err(InvalidCharacterInCrateName { span: sp, character: c, crate_name: s }); + sess.emit_err(InvalidCharacterInCrateName { + span: sp, + character: c, + crate_name: s, + crate_name_help: if sp.is_none() { + Some(InvalidCrateNameHelp::AddCrateName) + } else { + None + }, + }); } } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 194f7201ff3..bca49981668 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, @@ -117,6 +117,7 @@ pub fn feature_err_issue( /// Construct a future incompatibility diagnostic for a feature gate. /// /// This diagnostic is only a warning and *does not cause compilation to fail*. +#[track_caller] pub fn feature_warn(sess: &ParseSess, feature: Symbol, span: Span, explain: &'static str) { feature_warn_issue(sess, feature, span, GateIssue::Language, explain); } @@ -129,6 +130,7 @@ pub fn feature_warn(sess: &ParseSess, feature: Symbol, span: Span, explain: &'st /// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`. #[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::untranslatable_diagnostic)] +#[track_caller] pub fn feature_warn_issue( sess: &ParseSess, feature: Symbol, @@ -222,14 +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, - ); + let handler = Handler::with_tty_emitter(Some(sm.clone()), fallback_bundle); ParseSess::with_span_handler(handler, sm) } @@ -259,13 +254,9 @@ impl ParseSess { pub fn with_silent_emitter(fatal_note: Option<String>) -> 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); - let handler = Handler::with_emitter( - false, - None, - Box::new(SilentEmitter { fatal_handler, fatal_note }), - ); + 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) } @@ -351,6 +342,7 @@ impl ParseSess { self.create_warning(warning).emit() } + #[track_caller] pub fn create_note<'a>( &'a self, note: impl IntoDiagnostic<'a, Noted>, @@ -358,10 +350,12 @@ impl ParseSess { note.into_diagnostic(&self.span_diagnostic) } + #[track_caller] pub fn emit_note<'a>(&'a self, note: impl IntoDiagnostic<'a, Noted>) -> Noted { self.create_note(note).emit() } + #[track_caller] pub fn create_fatal<'a>( &'a self, fatal: impl IntoDiagnostic<'a, !>, @@ -369,6 +363,7 @@ impl ParseSess { fatal.into_diagnostic(&self.span_diagnostic) } + #[track_caller] pub fn emit_fatal<'a>(&'a self, fatal: impl IntoDiagnostic<'a, !>) -> ! { self.create_fatal(fatal).emit() } @@ -383,16 +378,19 @@ impl ParseSess { } #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> { self.span_diagnostic.struct_warn(msg) } #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> { self.span_diagnostic.struct_fatal(msg) } #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_diagnostic<G: EmissionGuarantee>( &self, msg: impl Into<DiagnosticMessage>, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 5be122ffbde..ad22e7c703d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -677,7 +677,7 @@ impl Session { pub fn delay_span_bug<S: Into<MultiSpan>>( &self, sp: S, - msg: impl Into<DiagnosticMessage>, + msg: impl Into<String>, ) -> ErrorGuaranteed { self.diagnostic().delay_span_bug(sp, msg) } @@ -995,18 +995,18 @@ impl Session { } /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(&self) -> bool { - self.edition().rust_2018() + pub fn at_least_rust_2018(&self) -> bool { + self.edition().at_least_rust_2018() } /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(&self) -> bool { - self.edition().rust_2021() + pub fn at_least_rust_2021(&self) -> bool { + self.edition().at_least_rust_2021() } /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(&self) -> bool { - self.edition().rust_2024() + pub fn at_least_rust_2024(&self) -> bool { + self.edition().at_least_rust_2024() } /// Returns `true` if we should use the PLT for shared library calls. @@ -1057,6 +1057,10 @@ impl Session { self.opts.unstable_opts.verbose } + pub fn print_llvm_stats(&self) -> bool { + self.opts.unstable_opts.print_codegen_stats + } + pub fn verify_llvm_ir(&self) -> bool { self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some() } @@ -1392,6 +1396,7 @@ pub fn build_session( file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>, target_override: Option<Target>, cfg_version: &'static str, + ice_file: Option<PathBuf>, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override // normal diagnostic warnings, since the warning lint can also be denied and changed @@ -1420,7 +1425,7 @@ pub fn build_session( let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let hash_kind = sopts.unstable_opts.src_hash_algorithm.unwrap_or_else(|| { if target_cfg.is_like_msvc { - SourceFileHashAlgorithm::Sha1 + SourceFileHashAlgorithm::Sha256 } else { SourceFileHashAlgorithm::Md5 } @@ -1437,10 +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), - ); + 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 { @@ -1731,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) } + Self { handler: rustc_errors::Handler::with_emitter(emitter) } } pub fn abort_if_errors(&self) { @@ -1745,7 +1751,7 @@ impl EarlyErrorHandler { self.handler.abort_if_errors(); let emitter = mk_emitter(output); - self.handler = Handler::with_emitter(true, None, emitter); + self.handler = Handler::with_emitter(emitter); } #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 1d15e2c28d8..71f2591fe66 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -7,6 +7,7 @@ impl Session { pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> { self.prof.verbose_generic_activity(what) } + /// Used by `-Z self-profile`. pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R { self.prof.verbose_generic_activity(what).run(f) } diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 527d5220564..6a26a4a22ed 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -31,6 +31,38 @@ pub fn adt_def(did: DefId) -> stable_mir::ty::AdtDef { with_tables(|t| t.adt_def(did)) } +pub fn foreign_def(did: DefId) -> stable_mir::ty::ForeignDef { + with_tables(|t| t.foreign_def(did)) +} + +pub fn fn_def(did: DefId) -> stable_mir::ty::FnDef { + with_tables(|t| t.fn_def(did)) +} + +pub fn closure_def(did: DefId) -> stable_mir::ty::ClosureDef { + with_tables(|t| t.closure_def(did)) +} + +pub fn generator_def(did: DefId) -> stable_mir::ty::GeneratorDef { + with_tables(|t| t.generator_def(did)) +} + +pub fn alias_def(did: DefId) -> stable_mir::ty::AliasDef { + with_tables(|t| t.alias_def(did)) +} + +pub fn param_def(did: DefId) -> stable_mir::ty::ParamDef { + with_tables(|t| t.param_def(did)) +} + +pub fn br_named_def(did: DefId) -> stable_mir::ty::BrNamedDef { + with_tables(|t| t.br_named_def(did)) +} + +pub fn trait_def(did: DefId) -> stable_mir::ty::TraitDef { + with_tables(|t| t.trait_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -44,6 +76,38 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::AdtDef(self.create_def_id(did)) } + pub fn foreign_def(&mut self, did: DefId) -> stable_mir::ty::ForeignDef { + stable_mir::ty::ForeignDef(self.create_def_id(did)) + } + + pub fn fn_def(&mut self, did: DefId) -> stable_mir::ty::FnDef { + stable_mir::ty::FnDef(self.create_def_id(did)) + } + + pub fn closure_def(&mut self, did: DefId) -> stable_mir::ty::ClosureDef { + stable_mir::ty::ClosureDef(self.create_def_id(did)) + } + + pub fn generator_def(&mut self, did: DefId) -> stable_mir::ty::GeneratorDef { + stable_mir::ty::GeneratorDef(self.create_def_id(did)) + } + + pub fn alias_def(&mut self, did: DefId) -> stable_mir::ty::AliasDef { + stable_mir::ty::AliasDef(self.create_def_id(did)) + } + + pub fn param_def(&mut self, did: DefId) -> stable_mir::ty::ParamDef { + stable_mir::ty::ParamDef(self.create_def_id(did)) + } + + pub fn br_named_def(&mut self, did: DefId) -> stable_mir::ty::BrNamedDef { + stable_mir::ty::BrNamedDef(self.create_def_id(did)) + } + + pub fn trait_def(&mut self, did: DefId) -> stable_mir::ty::TraitDef { + stable_mir::ty::TraitDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 6af6cfe58f1..c4bdec0ee28 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -8,10 +8,13 @@ //! For now, we are developing everything inside `rustc`, thus, we keep this module private. use crate::rustc_internal::{self, opaque}; -use crate::stable_mir::ty::{AdtSubsts, FloatTy, GenericArgKind, IntTy, RigidTy, TyKind, UintTy}; +use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx}; +use crate::stable_mir::ty::{FloatTy, IntTy, Movability, RigidTy, TyKind, UintTy}; use crate::stable_mir::{self, Context}; -use rustc_middle::mir; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_hir as hir; +use rustc_middle::mir::coverage::CodeRegion; +use rustc_middle::mir::{self}; +use rustc_middle::ty::{self, Ty, TyCtxt, Variance}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_target::abi::FieldIdx; use tracing::debug; @@ -46,8 +49,12 @@ impl<'tcx> Context for Tables<'tcx> { .basic_blocks .iter() .map(|block| stable_mir::mir::BasicBlock { - terminator: block.terminator().stable(), - statements: block.statements.iter().map(mir::Statement::stable).collect(), + terminator: block.terminator().stable(self), + statements: block + .statements + .iter() + .map(|statement| statement.stable(self)) + .collect(), }) .collect(), locals: mir.local_decls.iter().map(|decl| self.intern_ty(decl.ty)).collect(), @@ -59,7 +66,8 @@ impl<'tcx> Context for Tables<'tcx> { } fn ty_kind(&mut self, ty: crate::stable_mir::ty::Ty) -> TyKind { - self.rustc_ty_to_ty(self.types[ty.0]) + let ty = self.types[ty.0]; + ty.stable(self) } } @@ -70,79 +78,6 @@ pub struct Tables<'tcx> { } impl<'tcx> Tables<'tcx> { - fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind { - match ty.kind() { - ty::Bool => TyKind::RigidTy(RigidTy::Bool), - ty::Char => TyKind::RigidTy(RigidTy::Char), - ty::Int(int_ty) => match int_ty { - ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)), - ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)), - ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)), - ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)), - ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)), - ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)), - }, - ty::Uint(uint_ty) => match uint_ty { - ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)), - ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)), - ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)), - ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)), - ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)), - ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)), - }, - ty::Float(float_ty) => match float_ty { - ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)), - ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)), - }, - ty::Adt(adt_def, substs) => TyKind::RigidTy(RigidTy::Adt( - rustc_internal::adt_def(adt_def.did()), - AdtSubsts( - substs - .iter() - .map(|arg| match arg.unpack() { - ty::GenericArgKind::Lifetime(region) => { - GenericArgKind::Lifetime(opaque(®ion)) - } - ty::GenericArgKind::Type(ty) => { - GenericArgKind::Type(self.intern_ty(ty)) - } - ty::GenericArgKind::Const(const_) => { - GenericArgKind::Const(opaque(&const_)) - } - }) - .collect(), - ), - )), - ty::Foreign(_) => todo!(), - ty::Str => TyKind::RigidTy(RigidTy::Str), - ty::Array(ty, constant) => { - TyKind::RigidTy(RigidTy::Array(self.intern_ty(*ty), opaque(constant))) - } - ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(self.intern_ty(*ty))), - ty::RawPtr(_) => todo!(), - ty::Ref(_, _, _) => todo!(), - ty::FnDef(_, _) => todo!(), - ty::FnPtr(_) => todo!(), - ty::Dynamic(_, _, _) => todo!(), - ty::Closure(_, _) => todo!(), - ty::Generator(_, _, _) => todo!(), - ty::Never => todo!(), - ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( - fields.iter().map(|ty| self.intern_ty(ty)).collect(), - )), - ty::Alias(_, _) => todo!(), - ty::Param(_) => todo!(), - ty::Bound(_, _) => todo!(), - ty::Placeholder(..) - | ty::GeneratorWitness(_) - | ty::GeneratorWitnessMIR(_, _) - | ty::Infer(_) - | ty::Error(_) => { - unreachable!(); - } - } - } - fn intern_ty(&mut self, ty: Ty<'tcx>) -> stable_mir::ty::Ty { if let Some(id) = self.types.iter().position(|&t| t == ty) { return stable_mir::ty::Ty(id); @@ -162,76 +97,114 @@ fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate { } /// Trait used to convert between an internal MIR type to a Stable MIR type. -pub(crate) trait Stable { +pub(crate) trait Stable<'tcx> { /// The stable representation of the type implementing Stable. type T; /// Converts an object to the equivalent Stable MIR representation. - fn stable(&self) -> Self::T; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T; } -impl<'tcx> Stable for mir::Statement<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> { type T = stable_mir::mir::Statement; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::StatementKind::*; match &self.kind { Assign(assign) => { - stable_mir::mir::Statement::Assign(assign.0.stable(), assign.1.stable()) - } - FakeRead(_) => todo!(), - SetDiscriminant { .. } => todo!(), - Deinit(_) => todo!(), - StorageLive(_) => todo!(), - StorageDead(_) => todo!(), - Retag(_, _) => todo!(), - PlaceMention(_) => todo!(), - AscribeUserType(_, _) => todo!(), - Coverage(_) => todo!(), - Intrinsic(_) => todo!(), - ConstEvalCounter => todo!(), + stable_mir::mir::Statement::Assign(assign.0.stable(tables), assign.1.stable(tables)) + } + FakeRead(fake_read_place) => stable_mir::mir::Statement::FakeRead( + fake_read_place.0.stable(tables), + fake_read_place.1.stable(tables), + ), + SetDiscriminant { place: plc, variant_index: idx } => { + stable_mir::mir::Statement::SetDiscriminant { + place: plc.as_ref().stable(tables), + variant_index: idx.stable(tables), + } + } + Deinit(place) => stable_mir::mir::Statement::Deinit(place.stable(tables)), + StorageLive(place) => stable_mir::mir::Statement::StorageLive(place.stable(tables)), + StorageDead(place) => stable_mir::mir::Statement::StorageDead(place.stable(tables)), + Retag(retag, place) => { + stable_mir::mir::Statement::Retag(retag.stable(tables), place.stable(tables)) + } + PlaceMention(place) => stable_mir::mir::Statement::PlaceMention(place.stable(tables)), + AscribeUserType(place_projection, variance) => { + stable_mir::mir::Statement::AscribeUserType { + place: place_projection.as_ref().0.stable(tables), + projections: place_projection.as_ref().1.stable(tables), + variance: variance.stable(tables), + } + } + Coverage(coverage) => stable_mir::mir::Statement::Coverage(stable_mir::mir::Coverage { + kind: coverage.kind.stable(tables), + code_region: coverage.code_region.as_ref().map(|reg| reg.stable(tables)), + }), + Intrinsic(intrinstic) => { + stable_mir::mir::Statement::Intrinsic(intrinstic.stable(tables)) + } + ConstEvalCounter => stable_mir::mir::Statement::ConstEvalCounter, Nop => stable_mir::mir::Statement::Nop, } } } -impl<'tcx> Stable for mir::Rvalue<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { type T = stable_mir::mir::Rvalue; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::Rvalue::*; match self { - Use(op) => stable_mir::mir::Rvalue::Use(op.stable()), - Repeat(_, _) => todo!(), - Ref(region, kind, place) => { - stable_mir::mir::Rvalue::Ref(opaque(region), kind.stable(), place.stable()) - } + Use(op) => stable_mir::mir::Rvalue::Use(op.stable(tables)), + Repeat(op, len) => stable_mir::mir::Rvalue::Repeat(op.stable(tables), opaque(len)), + Ref(region, kind, place) => stable_mir::mir::Rvalue::Ref( + opaque(region), + kind.stable(tables), + place.stable(tables), + ), ThreadLocalRef(def_id) => { stable_mir::mir::Rvalue::ThreadLocalRef(rustc_internal::crate_item(*def_id)) } AddressOf(mutability, place) => { - stable_mir::mir::Rvalue::AddressOf(mutability.stable(), place.stable()) - } - Len(place) => stable_mir::mir::Rvalue::Len(place.stable()), - Cast(_, _, _) => todo!(), - BinaryOp(bin_op, ops) => { - stable_mir::mir::Rvalue::BinaryOp(bin_op.stable(), ops.0.stable(), ops.1.stable()) + stable_mir::mir::Rvalue::AddressOf(mutability.stable(tables), place.stable(tables)) } + Len(place) => stable_mir::mir::Rvalue::Len(place.stable(tables)), + Cast(cast_kind, op, ty) => stable_mir::mir::Rvalue::Cast( + cast_kind.stable(tables), + op.stable(tables), + tables.intern_ty(*ty), + ), + BinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::BinaryOp( + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), + ), CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp( - bin_op.stable(), - ops.0.stable(), - ops.1.stable(), + bin_op.stable(tables), + ops.0.stable(tables), + ops.1.stable(tables), ), - NullaryOp(_, _) => todo!(), - UnaryOp(un_op, op) => stable_mir::mir::Rvalue::UnaryOp(un_op.stable(), op.stable()), - Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable()), - Aggregate(_, _) => todo!(), - ShallowInitBox(_, _) => todo!(), - CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable()), + NullaryOp(null_op, ty) => { + stable_mir::mir::Rvalue::NullaryOp(null_op.stable(tables), tables.intern_ty(*ty)) + } + UnaryOp(un_op, op) => { + stable_mir::mir::Rvalue::UnaryOp(un_op.stable(tables), op.stable(tables)) + } + Discriminant(place) => stable_mir::mir::Rvalue::Discriminant(place.stable(tables)), + Aggregate(agg_kind, operands) => { + let operands = operands.iter().map(|op| op.stable(tables)).collect(); + stable_mir::mir::Rvalue::Aggregate(agg_kind.stable(tables), operands) + } + ShallowInitBox(op, ty) => { + stable_mir::mir::Rvalue::ShallowInitBox(op.stable(tables), tables.intern_ty(*ty)) + } + CopyForDeref(place) => stable_mir::mir::Rvalue::CopyForDeref(place.stable(tables)), } } } -impl Stable for mir::Mutability { +impl<'tcx> Stable<'tcx> for mir::Mutability { type T = stable_mir::mir::Mutability; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::Mutability::*; match *self { Not => stable_mir::mir::Mutability::Not, @@ -240,21 +213,21 @@ impl Stable for mir::Mutability { } } -impl Stable for mir::BorrowKind { +impl<'tcx> Stable<'tcx> for mir::BorrowKind { type T = stable_mir::mir::BorrowKind; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::BorrowKind::*; match *self { Shared => stable_mir::mir::BorrowKind::Shared, Shallow => stable_mir::mir::BorrowKind::Shallow, - Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable() }, + Mut { kind } => stable_mir::mir::BorrowKind::Mut { kind: kind.stable(tables) }, } } } -impl Stable for mir::MutBorrowKind { +impl<'tcx> Stable<'tcx> for mir::MutBorrowKind { type T = stable_mir::mir::MutBorrowKind; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::MutBorrowKind::*; match *self { Default => stable_mir::mir::MutBorrowKind::Default, @@ -264,28 +237,28 @@ impl Stable for mir::MutBorrowKind { } } -impl<'tcx> Stable for mir::NullOp<'tcx> { +impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::NullOp::*; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, - OffsetOf(indices) => { - stable_mir::mir::NullOp::OffsetOf(indices.iter().map(|idx| idx.stable()).collect()) - } + OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( + indices.iter().map(|idx| idx.stable(tables)).collect(), + ), } } } -impl Stable for mir::CastKind { +impl<'tcx> Stable<'tcx> for mir::CastKind { type T = stable_mir::mir::CastKind; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::CastKind::*; match self { PointerExposeAddress => stable_mir::mir::CastKind::PointerExposeAddress, PointerFromExposedAddress => stable_mir::mir::CastKind::PointerFromExposedAddress, - PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable()), + PointerCoercion(c) => stable_mir::mir::CastKind::PointerCoercion(c.stable(tables)), DynStar => stable_mir::mir::CastKind::DynStar, IntToInt => stable_mir::mir::CastKind::IntToInt, FloatToInt => stable_mir::mir::CastKind::FloatToInt, @@ -298,15 +271,102 @@ impl Stable for mir::CastKind { } } -impl Stable for ty::adjustment::PointerCoercion { +impl<'tcx> Stable<'tcx> for ty::AliasKind { + type T = stable_mir::ty::AliasKind; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use ty::AliasKind::*; + match self { + Projection => stable_mir::ty::AliasKind::Projection, + Inherent => stable_mir::ty::AliasKind::Inherent, + Opaque => stable_mir::ty::AliasKind::Opaque, + Weak => stable_mir::ty::AliasKind::Weak, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::AliasTy<'tcx> { + type T = stable_mir::ty::AliasTy; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let ty::AliasTy { args, def_id, .. } = self; + stable_mir::ty::AliasTy { def_id: tables.alias_def(*def_id), args: args.stable(tables) } + } +} + +impl<'tcx> Stable<'tcx> for ty::DynKind { + type T = stable_mir::ty::DynKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use ty::DynKind; + match self { + DynKind::Dyn => stable_mir::ty::DynKind::Dyn, + DynKind::DynStar => stable_mir::ty::DynKind::DynStar, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> { + type T = stable_mir::ty::ExistentialPredicate; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::ExistentialPredicate::*; + match self { + ty::ExistentialPredicate::Trait(existential_trait_ref) => { + Trait(existential_trait_ref.stable(tables)) + } + ty::ExistentialPredicate::Projection(existential_projection) => { + Projection(existential_projection.stable(tables)) + } + ty::ExistentialPredicate::AutoTrait(def_id) => AutoTrait(tables.trait_def(*def_id)), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> { + type T = stable_mir::ty::ExistentialTraitRef; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let ty::ExistentialTraitRef { def_id, args } = self; + stable_mir::ty::ExistentialTraitRef { + def_id: tables.trait_def(*def_id), + generic_args: args.stable(tables), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::TermKind<'tcx> { + type T = stable_mir::ty::TermKind; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::TermKind; + match self { + ty::TermKind::Ty(ty) => TermKind::Type(tables.intern_ty(*ty)), + ty::TermKind::Const(const_) => TermKind::Const(opaque(const_)), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> { + type T = stable_mir::ty::ExistentialProjection; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + let ty::ExistentialProjection { def_id, args, term } = self; + stable_mir::ty::ExistentialProjection { + def_id: tables.trait_def(*def_id), + generic_args: args.stable(tables), + term: term.unpack().stable(tables), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::adjustment::PointerCoercion { type T = stable_mir::mir::PointerCoercion; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use ty::adjustment::PointerCoercion; match self { PointerCoercion::ReifyFnPointer => stable_mir::mir::PointerCoercion::ReifyFnPointer, PointerCoercion::UnsafeFnPointer => stable_mir::mir::PointerCoercion::UnsafeFnPointer, PointerCoercion::ClosureFnPointer(unsafety) => { - stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable()) + stable_mir::mir::PointerCoercion::ClosureFnPointer(unsafety.stable(tables)) } PointerCoercion::MutToConstPointer => { stable_mir::mir::PointerCoercion::MutToConstPointer @@ -317,9 +377,9 @@ impl Stable for ty::adjustment::PointerCoercion { } } -impl Stable for rustc_hir::Unsafety { +impl<'tcx> Stable<'tcx> for rustc_hir::Unsafety { type T = stable_mir::mir::Safety; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { match self { rustc_hir::Unsafety::Unsafe => stable_mir::mir::Safety::Unsafe, rustc_hir::Unsafety::Normal => stable_mir::mir::Safety::Normal, @@ -327,28 +387,44 @@ impl Stable for rustc_hir::Unsafety { } } -impl Stable for FieldIdx { +impl<'tcx> Stable<'tcx> for mir::FakeReadCause { + type T = stable_mir::mir::FakeReadCause; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use mir::FakeReadCause::*; + match self { + ForMatchGuard => stable_mir::mir::FakeReadCause::ForMatchGuard, + ForMatchedPlace(local_def_id) => { + stable_mir::mir::FakeReadCause::ForMatchedPlace(opaque(local_def_id)) + } + ForGuardBinding => stable_mir::mir::FakeReadCause::ForGuardBinding, + ForLet(local_def_id) => stable_mir::mir::FakeReadCause::ForLet(opaque(local_def_id)), + ForIndex => stable_mir::mir::FakeReadCause::ForIndex, + } + } +} + +impl<'tcx> Stable<'tcx> for FieldIdx { type T = usize; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { self.as_usize() } } -impl<'tcx> Stable for mir::Operand<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> { type T = stable_mir::mir::Operand; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use mir::Operand::*; match self { - Copy(place) => stable_mir::mir::Operand::Copy(place.stable()), - Move(place) => stable_mir::mir::Operand::Move(place.stable()), + Copy(place) => stable_mir::mir::Operand::Copy(place.stable(tables)), + Move(place) => stable_mir::mir::Operand::Move(place.stable(tables)), Constant(c) => stable_mir::mir::Operand::Constant(c.to_string()), } } } -impl<'tcx> Stable for mir::Place<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Place<'tcx> { type T = stable_mir::mir::Place; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { stable_mir::mir::Place { local: self.local.as_usize(), projection: format!("{:?}", self.projection), @@ -356,9 +432,113 @@ impl<'tcx> Stable for mir::Place<'tcx> { } } -impl Stable for mir::UnwindAction { +impl<'tcx> Stable<'tcx> for mir::coverage::CoverageKind { + type T = stable_mir::mir::CoverageKind; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_middle::mir::coverage::CoverageKind; + match self { + CoverageKind::Counter { function_source_hash, id } => { + stable_mir::mir::CoverageKind::Counter { + function_source_hash: *function_source_hash as usize, + id: opaque(id), + } + } + CoverageKind::Expression { id, lhs, op, rhs } => { + stable_mir::mir::CoverageKind::Expression { + id: opaque(id), + lhs: opaque(lhs), + op: op.stable(tables), + rhs: opaque(rhs), + } + } + CoverageKind::Unreachable => stable_mir::mir::CoverageKind::Unreachable, + } + } +} + +impl<'tcx> Stable<'tcx> for mir::UserTypeProjection { + type T = stable_mir::mir::UserTypeProjection; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + UserTypeProjection { base: self.base.as_usize(), projection: format!("{:?}", self.projs) } + } +} + +impl<'tcx> Stable<'tcx> for mir::coverage::Op { + type T = stable_mir::mir::Op; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use rustc_middle::mir::coverage::Op::*; + match self { + Subtract => stable_mir::mir::Op::Subtract, + Add => stable_mir::mir::Op::Add, + } + } +} + +impl<'tcx> Stable<'tcx> for mir::Local { + type T = stable_mir::mir::Local; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + self.as_usize() + } +} + +impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx { + type T = VariantIdx; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + self.as_usize() + } +} + +impl<'tcx> Stable<'tcx> for Variance { + type T = stable_mir::mir::Variance; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + Variance::Bivariant => stable_mir::mir::Variance::Bivariant, + Variance::Contravariant => stable_mir::mir::Variance::Contravariant, + Variance::Covariant => stable_mir::mir::Variance::Covariant, + Variance::Invariant => stable_mir::mir::Variance::Invariant, + } + } +} + +impl<'tcx> Stable<'tcx> for mir::RetagKind { + type T = stable_mir::mir::RetagKind; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use rustc_middle::mir::RetagKind; + match self { + RetagKind::FnEntry => stable_mir::mir::RetagKind::FnEntry, + RetagKind::TwoPhase => stable_mir::mir::RetagKind::TwoPhase, + RetagKind::Raw => stable_mir::mir::RetagKind::Raw, + RetagKind::Default => stable_mir::mir::RetagKind::Default, + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_middle::ty::UserTypeAnnotationIndex { + type T = usize; + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + self.as_usize() + } +} + +impl<'tcx> Stable<'tcx> for CodeRegion { + type T = stable_mir::mir::CodeRegion; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + stable_mir::mir::CodeRegion { + file_name: self.file_name.as_str().to_string(), + start_line: self.start_line as usize, + start_col: self.start_col as usize, + end_line: self.end_line as usize, + end_col: self.end_col as usize, + } + } +} + +impl<'tcx> Stable<'tcx> for mir::UnwindAction { type T = stable_mir::mir::UnwindAction; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::UnwindAction; match self { UnwindAction::Continue => stable_mir::mir::UnwindAction::Continue, @@ -369,46 +549,68 @@ impl Stable for mir::UnwindAction { } } -impl<'tcx> Stable for mir::AssertMessage<'tcx> { +impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> { + type T = stable_mir::mir::NonDivergingIntrinsic; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_middle::mir::NonDivergingIntrinsic; + match self { + NonDivergingIntrinsic::Assume(op) => { + stable_mir::mir::NonDivergingIntrinsic::Assume(op.stable(tables)) + } + NonDivergingIntrinsic::CopyNonOverlapping(copy_non_overlapping) => { + stable_mir::mir::NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { + src: copy_non_overlapping.src.stable(tables), + dst: copy_non_overlapping.dst.stable(tables), + count: copy_non_overlapping.count.stable(tables), + }) + } + } + } +} + +impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> { type T = stable_mir::mir::AssertMessage; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::AssertKind; match self { AssertKind::BoundsCheck { len, index } => stable_mir::mir::AssertMessage::BoundsCheck { - len: len.stable(), - index: index.stable(), + len: len.stable(tables), + index: index.stable(tables), }, AssertKind::Overflow(bin_op, op1, op2) => stable_mir::mir::AssertMessage::Overflow( - bin_op.stable(), - op1.stable(), - op2.stable(), + bin_op.stable(tables), + op1.stable(tables), + op2.stable(tables), ), - AssertKind::OverflowNeg(op) => stable_mir::mir::AssertMessage::OverflowNeg(op.stable()), + AssertKind::OverflowNeg(op) => { + stable_mir::mir::AssertMessage::OverflowNeg(op.stable(tables)) + } AssertKind::DivisionByZero(op) => { - stable_mir::mir::AssertMessage::DivisionByZero(op.stable()) + stable_mir::mir::AssertMessage::DivisionByZero(op.stable(tables)) } AssertKind::RemainderByZero(op) => { - stable_mir::mir::AssertMessage::RemainderByZero(op.stable()) + stable_mir::mir::AssertMessage::RemainderByZero(op.stable(tables)) } AssertKind::ResumedAfterReturn(generator) => { - stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable()) + stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable(tables)) } AssertKind::ResumedAfterPanic(generator) => { - stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable()) + stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable(tables)) } AssertKind::MisalignedPointerDereference { required, found } => { stable_mir::mir::AssertMessage::MisalignedPointerDereference { - required: required.stable(), - found: found.stable(), + required: required.stable(tables), + found: found.stable(tables), } } } } } -impl Stable for mir::BinOp { +impl<'tcx> Stable<'tcx> for mir::BinOp { type T = stable_mir::mir::BinOp; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::BinOp; match self { BinOp::Add => stable_mir::mir::BinOp::Add, @@ -437,9 +639,9 @@ impl Stable for mir::BinOp { } } -impl Stable for mir::UnOp { +impl<'tcx> Stable<'tcx> for mir::UnOp { type T = stable_mir::mir::UnOp; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use mir::UnOp; match self { UnOp::Not => stable_mir::mir::UnOp::Not, @@ -448,9 +650,43 @@ impl Stable for mir::UnOp { } } -impl Stable for rustc_hir::GeneratorKind { +impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { + type T = stable_mir::mir::AggregateKind; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self { + mir::AggregateKind::Array(ty) => { + stable_mir::mir::AggregateKind::Array(tables.intern_ty(*ty)) + } + mir::AggregateKind::Tuple => stable_mir::mir::AggregateKind::Tuple, + mir::AggregateKind::Adt(def_id, var_idx, generic_arg, user_ty_index, field_idx) => { + stable_mir::mir::AggregateKind::Adt( + rustc_internal::adt_def(*def_id), + var_idx.index(), + generic_arg.stable(tables), + user_ty_index.map(|idx| idx.index()), + field_idx.map(|idx| idx.index()), + ) + } + mir::AggregateKind::Closure(def_id, generic_arg) => { + stable_mir::mir::AggregateKind::Closure( + rustc_internal::closure_def(*def_id), + generic_arg.stable(tables), + ) + } + mir::AggregateKind::Generator(def_id, generic_arg, movability) => { + stable_mir::mir::AggregateKind::Generator( + rustc_internal::generator_def(*def_id), + generic_arg.stable(tables), + movability.stable(tables), + ) + } + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_hir::GeneratorKind { type T = stable_mir::mir::GeneratorKind; - fn stable(&self) -> Self::T { + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; match self { GeneratorKind::Async(async_gen) => { @@ -466,35 +702,35 @@ impl Stable for rustc_hir::GeneratorKind { } } -impl<'tcx> Stable for mir::InlineAsmOperand<'tcx> { +impl<'tcx> Stable<'tcx> for mir::InlineAsmOperand<'tcx> { type T = stable_mir::mir::InlineAsmOperand; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::InlineAsmOperand; let (in_value, out_place) = match self { - InlineAsmOperand::In { value, .. } => (Some(value.stable()), None), - InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable())), + InlineAsmOperand::In { value, .. } => (Some(value.stable(tables)), None), + InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable(tables))), InlineAsmOperand::InOut { in_value, out_place, .. } => { - (Some(in_value.stable()), out_place.map(|place| place.stable())) + (Some(in_value.stable(tables)), out_place.map(|place| place.stable(tables))) } InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => (None, None), }; - stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{:?}", self) } + stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{self:?}") } } } -impl<'tcx> Stable for mir::Terminator<'tcx> { +impl<'tcx> Stable<'tcx> for mir::Terminator<'tcx> { type T = stable_mir::mir::Terminator; - fn stable(&self) -> Self::T { + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { use rustc_middle::mir::TerminatorKind::*; use stable_mir::mir::Terminator; match &self.kind { Goto { target } => Terminator::Goto { target: target.as_usize() }, SwitchInt { discr, targets } => Terminator::SwitchInt { - discr: discr.stable(), + discr: discr.stable(tables), targets: targets .iter() .map(|(value, target)| stable_mir::mir::SwitchTarget { @@ -509,37 +745,321 @@ impl<'tcx> Stable for mir::Terminator<'tcx> { Return => Terminator::Return, Unreachable => Terminator::Unreachable, Drop { place, target, unwind, replace: _ } => Terminator::Drop { - place: place.stable(), + place: place.stable(tables), target: target.as_usize(), - unwind: unwind.stable(), + unwind: unwind.stable(tables), }, Call { func, args, destination, target, unwind, call_source: _, fn_span: _ } => { Terminator::Call { - func: func.stable(), - args: args.iter().map(|arg| arg.stable()).collect(), - destination: destination.stable(), + func: func.stable(tables), + args: args.iter().map(|arg| arg.stable(tables)).collect(), + destination: destination.stable(tables), target: target.map(|t| t.as_usize()), - unwind: unwind.stable(), + unwind: unwind.stable(tables), } } Assert { cond, expected, msg, target, unwind } => Terminator::Assert { - cond: cond.stable(), + cond: cond.stable(tables), expected: *expected, - msg: msg.stable(), + msg: msg.stable(tables), target: target.as_usize(), - unwind: unwind.stable(), + unwind: unwind.stable(tables), }, InlineAsm { template, operands, options, line_spans, destination, unwind } => { Terminator::InlineAsm { - template: format!("{:?}", template), - operands: operands.iter().map(|operand| operand.stable()).collect(), - options: format!("{:?}", options), - line_spans: format!("{:?}", line_spans), + template: format!("{template:?}"), + operands: operands.iter().map(|operand| operand.stable(tables)).collect(), + options: format!("{options:?}"), + line_spans: format!("{line_spans:?}"), destination: destination.map(|d| d.as_usize()), - unwind: unwind.stable(), + unwind: unwind.stable(tables), } } Yield { .. } | GeneratorDrop | FalseEdge { .. } | FalseUnwind { .. } => unreachable!(), } } } + +impl<'tcx> Stable<'tcx> for ty::GenericArgs<'tcx> { + type T = stable_mir::ty::GenericArgs; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::GenericArgs; + + GenericArgs(self.iter().map(|arg| arg.unpack().stable(tables)).collect()) + } +} + +impl<'tcx> Stable<'tcx> for ty::GenericArgKind<'tcx> { + type T = stable_mir::ty::GenericArgKind; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::GenericArgKind; + match self { + ty::GenericArgKind::Lifetime(region) => GenericArgKind::Lifetime(opaque(region)), + ty::GenericArgKind::Type(ty) => GenericArgKind::Type(tables.intern_ty(*ty)), + ty::GenericArgKind::Const(const_) => GenericArgKind::Const(opaque(&const_)), + } + } +} + +impl<'tcx, S, V> Stable<'tcx> for ty::Binder<'tcx, S> +where + S: Stable<'tcx, T = V>, +{ + type T = stable_mir::ty::Binder<V>; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::Binder; + + Binder { + value: self.as_ref().skip_binder().stable(tables), + bound_vars: self + .bound_vars() + .iter() + .map(|bound_var| bound_var.stable(tables)) + .collect(), + } + } +} + +impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { + type T = stable_mir::ty::FnSig; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use rustc_target::spec::abi; + use stable_mir::ty::{Abi, FnSig, Unsafety}; + + FnSig { + inputs_and_output: self + .inputs_and_output + .iter() + .map(|ty| tables.intern_ty(ty)) + .collect(), + c_variadic: self.c_variadic, + unsafety: match self.unsafety { + hir::Unsafety::Normal => Unsafety::Normal, + hir::Unsafety::Unsafe => Unsafety::Unsafe, + }, + abi: match self.abi { + abi::Abi::Rust => Abi::Rust, + abi::Abi::C { unwind } => Abi::C { unwind }, + abi::Abi::Cdecl { unwind } => Abi::Cdecl { unwind }, + abi::Abi::Stdcall { unwind } => Abi::Stdcall { unwind }, + abi::Abi::Fastcall { unwind } => Abi::Fastcall { unwind }, + abi::Abi::Vectorcall { unwind } => Abi::Vectorcall { unwind }, + abi::Abi::Thiscall { unwind } => Abi::Thiscall { unwind }, + abi::Abi::Aapcs { unwind } => Abi::Aapcs { unwind }, + abi::Abi::Win64 { unwind } => Abi::Win64 { unwind }, + abi::Abi::SysV64 { unwind } => Abi::SysV64 { unwind }, + abi::Abi::PtxKernel => Abi::PtxKernel, + abi::Abi::Msp430Interrupt => Abi::Msp430Interrupt, + abi::Abi::X86Interrupt => Abi::X86Interrupt, + abi::Abi::AmdGpuKernel => Abi::AmdGpuKernel, + abi::Abi::EfiApi => Abi::EfiApi, + abi::Abi::AvrInterrupt => Abi::AvrInterrupt, + abi::Abi::AvrNonBlockingInterrupt => Abi::AvrNonBlockingInterrupt, + abi::Abi::CCmseNonSecureCall => Abi::CCmseNonSecureCall, + abi::Abi::Wasm => Abi::Wasm, + abi::Abi::System { unwind } => Abi::System { unwind }, + abi::Abi::RustIntrinsic => Abi::RustIntrinsic, + abi::Abi::RustCall => Abi::RustCall, + abi::Abi::PlatformIntrinsic => Abi::PlatformIntrinsic, + abi::Abi::Unadjusted => Abi::Unadjusted, + abi::Abi::RustCold => Abi::RustCold, + }, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::BoundTyKind { + type T = stable_mir::ty::BoundTyKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundTyKind; + + match self { + ty::BoundTyKind::Anon => BoundTyKind::Anon, + ty::BoundTyKind::Param(def_id, symbol) => { + BoundTyKind::Param(rustc_internal::param_def(*def_id), symbol.to_string()) + } + } + } +} + +impl<'tcx> Stable<'tcx> for ty::BoundRegionKind { + type T = stable_mir::ty::BoundRegionKind; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundRegionKind; + + match self { + ty::BoundRegionKind::BrAnon(option_span) => { + BoundRegionKind::BrAnon(option_span.map(|span| opaque(&span))) + } + ty::BoundRegionKind::BrNamed(def_id, symbol) => { + BoundRegionKind::BrNamed(rustc_internal::br_named_def(*def_id), symbol.to_string()) + } + ty::BoundRegionKind::BrEnv => BoundRegionKind::BrEnv, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::BoundVariableKind { + type T = stable_mir::ty::BoundVariableKind; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::BoundVariableKind; + + match self { + ty::BoundVariableKind::Ty(bound_ty_kind) => { + BoundVariableKind::Ty(bound_ty_kind.stable(tables)) + } + ty::BoundVariableKind::Region(bound_region_kind) => { + BoundVariableKind::Region(bound_region_kind.stable(tables)) + } + ty::BoundVariableKind::Const => BoundVariableKind::Const, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::IntTy { + type T = IntTy; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + ty::IntTy::Isize => IntTy::Isize, + ty::IntTy::I8 => IntTy::I8, + ty::IntTy::I16 => IntTy::I16, + ty::IntTy::I32 => IntTy::I32, + ty::IntTy::I64 => IntTy::I64, + ty::IntTy::I128 => IntTy::I128, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::UintTy { + type T = UintTy; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + ty::UintTy::Usize => UintTy::Usize, + ty::UintTy::U8 => UintTy::U8, + ty::UintTy::U16 => UintTy::U16, + ty::UintTy::U32 => UintTy::U32, + ty::UintTy::U64 => UintTy::U64, + ty::UintTy::U128 => UintTy::U128, + } + } +} + +impl<'tcx> Stable<'tcx> for ty::FloatTy { + type T = FloatTy; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + ty::FloatTy::F32 => FloatTy::F32, + ty::FloatTy::F64 => FloatTy::F64, + } + } +} + +impl<'tcx> Stable<'tcx> for hir::Movability { + type T = Movability; + + fn stable(&self, _: &mut Tables<'tcx>) -> Self::T { + match self { + hir::Movability::Static => Movability::Static, + hir::Movability::Movable => Movability::Movable, + } + } +} + +impl<'tcx> Stable<'tcx> for Ty<'tcx> { + type T = stable_mir::ty::TyKind; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + match self.kind() { + ty::Bool => TyKind::RigidTy(RigidTy::Bool), + ty::Char => TyKind::RigidTy(RigidTy::Char), + ty::Int(int_ty) => TyKind::RigidTy(RigidTy::Int(int_ty.stable(tables))), + ty::Uint(uint_ty) => TyKind::RigidTy(RigidTy::Uint(uint_ty.stable(tables))), + ty::Float(float_ty) => TyKind::RigidTy(RigidTy::Float(float_ty.stable(tables))), + ty::Adt(adt_def, generic_args) => TyKind::RigidTy(RigidTy::Adt( + rustc_internal::adt_def(adt_def.did()), + generic_args.stable(tables), + )), + ty::Foreign(def_id) => { + TyKind::RigidTy(RigidTy::Foreign(rustc_internal::foreign_def(*def_id))) + } + ty::Str => TyKind::RigidTy(RigidTy::Str), + ty::Array(ty, constant) => { + TyKind::RigidTy(RigidTy::Array(tables.intern_ty(*ty), opaque(constant))) + } + ty::Slice(ty) => TyKind::RigidTy(RigidTy::Slice(tables.intern_ty(*ty))), + ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { + TyKind::RigidTy(RigidTy::RawPtr(tables.intern_ty(*ty), mutbl.stable(tables))) + } + ty::Ref(region, ty, mutbl) => TyKind::RigidTy(RigidTy::Ref( + opaque(region), + tables.intern_ty(*ty), + mutbl.stable(tables), + )), + ty::FnDef(def_id, generic_args) => TyKind::RigidTy(RigidTy::FnDef( + rustc_internal::fn_def(*def_id), + generic_args.stable(tables), + )), + ty::FnPtr(poly_fn_sig) => TyKind::RigidTy(RigidTy::FnPtr(poly_fn_sig.stable(tables))), + ty::Dynamic(existential_predicates, region, dyn_kind) => { + TyKind::RigidTy(RigidTy::Dynamic( + existential_predicates + .iter() + .map(|existential_predicate| existential_predicate.stable(tables)) + .collect(), + opaque(region), + dyn_kind.stable(tables), + )) + } + ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( + rustc_internal::closure_def(*def_id), + generic_args.stable(tables), + )), + ty::Generator(def_id, generic_args, movability) => TyKind::RigidTy(RigidTy::Generator( + rustc_internal::generator_def(*def_id), + generic_args.stable(tables), + movability.stable(tables), + )), + ty::Never => TyKind::RigidTy(RigidTy::Never), + ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple( + fields.iter().map(|ty| tables.intern_ty(ty)).collect(), + )), + ty::Alias(alias_kind, alias_ty) => { + TyKind::Alias(alias_kind.stable(tables), alias_ty.stable(tables)) + } + 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(_, _) + | ty::Infer(_) + | ty::Error(_) => { + unreachable!(); + } + } + } +} + +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/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index 02ac907f09a..c16bd6cbd70 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -1,4 +1,7 @@ use crate::rustc_internal::Opaque; +use crate::stable_mir::ty::{ + AdtDef, ClosureDef, Const, GeneratorDef, GenericArgs, Movability, Region, +}; use crate::stable_mir::{self, ty::Ty}; #[derive(Clone, Debug)] @@ -131,15 +134,104 @@ pub enum AsyncGeneratorKind { Fn, } +pub(crate) type LocalDefId = Opaque; +pub(crate) type CounterValueReference = Opaque; +pub(crate) type InjectedExpressionId = Opaque; +pub(crate) type ExpressionOperandId = Opaque; + +/// The FakeReadCause describes the type of pattern why a FakeRead statement exists. +#[derive(Clone, Debug)] +pub enum FakeReadCause { + ForMatchGuard, + ForMatchedPlace(LocalDefId), + ForGuardBinding, + ForLet(LocalDefId), + ForIndex, +} + +/// Describes what kind of retag is to be performed +#[derive(Clone, Debug)] +pub enum RetagKind { + FnEntry, + TwoPhase, + Raw, + Default, +} + +#[derive(Clone, Debug)] +pub enum Variance { + Covariant, + Invariant, + Contravariant, + Bivariant, +} + +#[derive(Clone, Debug)] +pub enum Op { + Subtract, + Add, +} + +#[derive(Clone, Debug)] +pub enum CoverageKind { + Counter { + function_source_hash: usize, + id: CounterValueReference, + }, + Expression { + id: InjectedExpressionId, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + }, + Unreachable, +} + +#[derive(Clone, Debug)] +pub struct CodeRegion { + pub file_name: String, + pub start_line: usize, + pub start_col: usize, + pub end_line: usize, + pub end_col: usize, +} + +#[derive(Clone, Debug)] +pub struct Coverage { + pub kind: CoverageKind, + pub code_region: Option<CodeRegion>, +} + +#[derive(Clone, Debug)] +pub struct CopyNonOverlapping { + pub src: Operand, + pub dst: Operand, + pub count: Operand, +} + +#[derive(Clone, Debug)] +pub enum NonDivergingIntrinsic { + Assume(Operand), + CopyNonOverlapping(CopyNonOverlapping), +} + #[derive(Clone, Debug)] pub enum Statement { Assign(Place, Rvalue), + FakeRead(FakeReadCause, Place), + SetDiscriminant { place: Place, variant_index: VariantIdx }, + Deinit(Place), + StorageLive(Local), + StorageDead(Local), + Retag(RetagKind, Place), + PlaceMention(Place), + AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance }, + Coverage(Coverage), + Intrinsic(NonDivergingIntrinsic), + ConstEvalCounter, Nop, } -type Region = Opaque; - -// FIXME this is incomplete #[derive(Clone, Debug)] pub enum Rvalue { /// Creates a pointer with the indicated mutability to the place. @@ -148,6 +240,16 @@ pub enum Rvalue { /// `&raw v` or `addr_of!(v)`. AddressOf(Mutability, Place), + /// Creates an aggregate value, like a tuple or struct. + /// + /// This is needed because dataflow analysis needs to distinguish + /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` + /// has a destructor. + /// + /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After + /// generator lowering, `Generator` aggregate kinds are disallowed too. + Aggregate(AggregateKind, Vec<Operand>), + /// * `Offset` has the same semantics as [`offset`](pointer::offset), except that the second /// parameter may be a `usize` as well. /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, @@ -200,6 +302,16 @@ pub enum Rvalue { /// Creates a reference to the place. Ref(Region, BorrowKind, Place), + /// Creates an array where each element is the value of the operand. + /// + /// This is the cause of a bug in the case where the repetition count is zero because the value + /// is not dropped, see [#74836]. + /// + /// Corresponds to source code like `[x; 32]`. + /// + /// [#74836]: https://github.com/rust-lang/rust/issues/74836 + Repeat(Operand, Const), + /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`. /// /// This is different from a normal transmute because dataflow analysis will treat the box as @@ -220,6 +332,9 @@ pub enum Rvalue { /// nature of this operation? ThreadLocalRef(stable_mir::CrateItem), + /// Computes a value as described by the operation. + NullaryOp(NullOp, Ty), + /// Exactly like `BinaryOp`, but less operands. /// /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; @@ -232,6 +347,15 @@ pub enum Rvalue { } #[derive(Clone, Debug)] +pub enum AggregateKind { + Array(Ty), + Tuple, + Adt(AdtDef, VariantIdx, GenericArgs, Option<UserTypeAnnotationIndex>, Option<FieldIdx>), + Closure(ClosureDef, GenericArgs), + Generator(GeneratorDef, GenericArgs, Movability), +} + +#[derive(Clone, Debug)] pub enum Operand { Copy(Place), Move(Place), @@ -240,12 +364,25 @@ pub enum Operand { #[derive(Clone, Debug)] pub struct Place { - pub local: usize, + pub local: Local, pub projection: String, } +#[derive(Clone, Debug)] +pub struct UserTypeProjection { + pub base: UserTypeAnnotationIndex, + pub projection: String, +} + +pub type Local = usize; + type FieldIdx = usize; +/// The source-order index of a variant in a type. +pub type VariantIdx = usize; + +type UserTypeAnnotationIndex = usize; + #[derive(Clone, Debug)] pub struct SwitchTarget { pub value: u128, diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index e9f17f92c04..025225b8d19 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -1,4 +1,4 @@ -use super::{with, DefId}; +use super::{mir::Mutability, with, DefId}; use crate::rustc_internal::Opaque; #[derive(Copy, Clone, Debug)] @@ -10,12 +10,16 @@ impl Ty { } } -type Const = Opaque; -type Region = Opaque; +pub(crate) type Const = Opaque; +pub(crate) type Region = Opaque; +type Span = Opaque; #[derive(Clone, Debug)] pub enum TyKind { RigidTy(RigidTy), + Alias(AliasKind, AliasTy), + Param(ParamTy), + Bound(usize, BoundTy), } #[derive(Clone, Debug)] @@ -25,10 +29,19 @@ pub enum RigidTy { Int(IntTy), Uint(UintTy), Float(FloatTy), - Adt(AdtDef, AdtSubsts), + Adt(AdtDef, GenericArgs), + Foreign(ForeignDef), Str, Array(Ty, Const), Slice(Ty), + RawPtr(Ty, Mutability), + Ref(Region, Ty, Mutability), + FnDef(FnDef, GenericArgs), + FnPtr(PolyFnSig), + Closure(ClosureDef, GenericArgs), + Generator(GeneratorDef, GenericArgs, Movability), + Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind), + Never, Tuple(Vec<Ty>), } @@ -58,17 +71,174 @@ pub enum FloatTy { F64, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Movability { + Static, + Movable, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ForeignDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FnDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ClosureDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct GeneratorDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ParamDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct BrNamedDef(pub(crate) DefId); + #[derive(Clone, PartialEq, Eq, Debug)] pub struct AdtDef(pub(crate) DefId); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct AliasDef(pub(crate) DefId); + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TraitDef(pub(crate) DefId); + #[derive(Clone, Debug)] -pub struct AdtSubsts(pub Vec<GenericArgKind>); +pub struct GenericArgs(pub Vec<GenericArgKind>); #[derive(Clone, Debug)] pub enum GenericArgKind { - // FIXME add proper region Lifetime(Region), Type(Ty), - // FIXME add proper const Const(Const), } + +#[derive(Clone, Debug)] +pub enum TermKind { + Type(Ty), + Const(Const), +} + +#[derive(Clone, Debug)] +pub enum AliasKind { + Projection, + Inherent, + Opaque, + Weak, +} + +#[derive(Clone, Debug)] +pub struct AliasTy { + pub def_id: AliasDef, + pub args: GenericArgs, +} + +pub type PolyFnSig = Binder<FnSig>; + +#[derive(Clone, Debug)] +pub struct FnSig { + pub inputs_and_output: Vec<Ty>, + pub c_variadic: bool, + pub unsafety: Unsafety, + pub abi: Abi, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Unsafety { + Unsafe, + Normal, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Abi { + Rust, + C { unwind: bool }, + Cdecl { unwind: bool }, + Stdcall { unwind: bool }, + Fastcall { unwind: bool }, + Vectorcall { unwind: bool }, + Thiscall { unwind: bool }, + Aapcs { unwind: bool }, + Win64 { unwind: bool }, + SysV64 { unwind: bool }, + PtxKernel, + Msp430Interrupt, + X86Interrupt, + AmdGpuKernel, + EfiApi, + AvrInterrupt, + AvrNonBlockingInterrupt, + CCmseNonSecureCall, + Wasm, + System { unwind: bool }, + RustIntrinsic, + RustCall, + PlatformIntrinsic, + Unadjusted, + RustCold, +} + +#[derive(Clone, Debug)] +pub struct Binder<T> { + pub value: T, + pub bound_vars: Vec<BoundVariableKind>, +} + +#[derive(Clone, Debug)] +pub enum BoundVariableKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum BoundTyKind { + Anon, + Param(ParamDef, String), +} + +#[derive(Clone, Debug)] +pub enum BoundRegionKind { + BrAnon(Option<Span>), + BrNamed(BrNamedDef, String), + BrEnv, +} + +#[derive(Clone, Debug)] +pub enum DynKind { + Dyn, + DynStar, +} + +#[derive(Clone, Debug)] +pub enum ExistentialPredicate { + Trait(ExistentialTraitRef), + Projection(ExistentialProjection), + AutoTrait(TraitDef), +} + +#[derive(Clone, Debug)] +pub struct ExistentialTraitRef { + pub def_id: TraitDef, + pub generic_args: GenericArgs, +} + +#[derive(Clone, Debug)] +pub struct ExistentialProjection { + pub def_id: TraitDef, + 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/edition.rs b/compiler/rustc_span/src/edition.rs index f16db69aae2..608b8c24bde 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -82,17 +82,17 @@ impl Edition { } /// Are we allowed to use features from the Rust 2018 edition? - pub fn rust_2018(self) -> bool { + pub fn at_least_rust_2018(self) -> bool { self >= Edition::Edition2018 } /// Are we allowed to use features from the Rust 2021 edition? - pub fn rust_2021(self) -> bool { + pub fn at_least_rust_2021(self) -> bool { self >= Edition::Edition2021 } /// Are we allowed to use features from the Rust 2024 edition? - pub fn rust_2024(self) -> bool { + pub fn at_least_rust_2024(self) -> bool { self >= Edition::Edition2024 } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5c56337d1e0..fbd9bf05528 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -605,7 +605,7 @@ impl Span { // FIXME: If this span comes from a `derive` macro but it points at code the user wrote, // the callsite span and the span will be pointing at different places. It also means that // we can safely provide suggestions on this span. - || (matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _)) + || (self.in_derive_expansion() && self.parent_callsite().map(|p| (p.lo(), p.hi())) != Some((self.lo(), self.hi()))) } @@ -707,24 +707,28 @@ impl Span { self.ctxt().edition() } + /// Is this edition 2015? #[inline] pub fn is_rust_2015(self) -> bool { self.edition().is_rust_2015() } + /// Are we allowed to use features from the Rust 2018 edition? #[inline] - pub fn rust_2018(self) -> bool { - self.edition().rust_2018() + pub fn at_least_rust_2018(self) -> bool { + self.edition().at_least_rust_2018() } + /// Are we allowed to use features from the Rust 2021 edition? #[inline] - pub fn rust_2021(self) -> bool { - self.edition().rust_2021() + pub fn at_least_rust_2021(self) -> bool { + self.edition().at_least_rust_2021() } + /// Are we allowed to use features from the Rust 2024 edition? #[inline] - pub fn rust_2024(self) -> bool { - self.edition().rust_2024() + pub fn at_least_rust_2024(self) -> bool { + self.edition().at_least_rust_2024() } /// Returns the source callee. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 08925761b39..d3739733c1d 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, @@ -619,6 +620,7 @@ symbols! { destruct, destructuring_assignment, diagnostic, + diagnostic_namespace, direct, discriminant_kind, discriminant_type, @@ -781,6 +783,7 @@ symbols! { generic_associated_types, generic_associated_types_extended, generic_const_exprs, + generic_const_items, generic_param_attrs, get_context, global_allocator, @@ -858,6 +861,7 @@ symbols! { item, item_like_imports, iter, + iter_mut, iter_repeat, iterator_collect_fn, kcfi, @@ -1367,9 +1371,13 @@ symbols! { simd_arith_offset, simd_as, simd_bitmask, + simd_bitreverse, + simd_bswap, simd_cast, simd_cast_ptr, simd_ceil, + simd_ctlz, + simd_cttz, simd_div, simd_eq, simd_expose_addr, 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 4a938ff938a..845b5791161 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<DictKey<'tcx>, usize>, options: EncodeTyOptions, ) -> String { - // L<element-type>[n]<element-value>E as literal argument + // L<element-type>[n][<element-value>]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(..) => { + // L<element-type>E 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<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) { - if value < zero { - s.push('n') - }; - let _ = write!(s, "{value}"); - } - - fn push_unsigned_value<T: Display>(s: &mut String, value: T) { - let _ = write!(s, "{value}"); - } + // Element type + s.push_str(&encode_ty(tcx, c.ty(), dict, options)); + } - 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()); + // Literal arguments + ty::ConstKind::Value(..) => { + // L<element-type>[n]<element-value>E as literal argument + + // 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 @@ -400,7 +411,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { let _ = write!(s, "{}", name.len()); // Prepend a '_' if name starts with a digit or '_' - if let Some(first) = name.as_bytes().get(0) { + if let Some(first) = name.as_bytes().first() { if first.is_ascii_digit() || *first == b'_' { s.push('_'); } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 03e7b3e7b40..3d2ea017d8f 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -693,8 +693,8 @@ impl<'a, Ty> FnAbi<'a, Ty> { "avr" => avr::compute_abi_info(self), "loongarch64" => loongarch::compute_abi_info(cx, self), "m68k" => m68k::compute_abi_info(self), - "mips" => mips::compute_abi_info(cx, self), - "mips64" => mips64::compute_abi_info(cx, self), + "mips" | "mips32r6" => mips::compute_abi_info(cx, self), + "mips64" | "mips64r6" => mips64::compute_abi_info(cx, self), "powerpc" => powerpc::compute_abi_info(self), "powerpc64" => powerpc64::compute_abi_info(cx, self), "s390x" => s390x::compute_abi_info(cx, self), diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 589cd3cf96b..084c917cc31 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -140,24 +140,3 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { offset } } - -impl<'a, Ty> TyAndLayout<'a, Ty> { - /// Returns `true` if the layout corresponds to an unsized type. - pub fn is_unsized(&self) -> bool { - self.abi.is_unsized() - } - - #[inline] - pub fn is_sized(&self) -> bool { - self.abi.is_sized() - } - - /// Returns `true` if the type is a ZST and not unsized. - pub fn is_zst(&self) -> bool { - match self.abi { - Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, - Abi::Uninhabited => self.size.bytes() == 0, - Abi::Aggregate { sized } => sized && self.size.bytes() == 0, - } - } -} diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index e60b8e78e5d..7c27732079b 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -238,8 +238,8 @@ impl FromStr for InlineAsmArch { "powerpc64" => Ok(Self::PowerPC64), "hexagon" => Ok(Self::Hexagon), "loongarch64" => Ok(Self::LoongArch64), - "mips" => Ok(Self::Mips), - "mips64" => Ok(Self::Mips64), + "mips" | "mips32r6" => Ok(Self::Mips), + "mips64" | "mips64r6" => Ok(Self::Mips64), "s390x" => Ok(Self::S390x), "spirv" => Ok(Self::SpirV), "wasm32" => Ok(Self::Wasm32), diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs index 87e8d627026..2414867be55 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -1,15 +1,15 @@ -use crate::spec::Target; +use crate::spec::{Target, TargetOptions}; pub fn target() -> Target { - let mut base = super::hermit_base::opts(); - base.max_atomic_width = Some(128); - base.features = "+v8a,+strict-align,+neon,+fp-armv8".into(); - Target { llvm_target: "aarch64-unknown-hermit".into(), pointer_width: 64, - data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: "aarch64".into(), - options: base, + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), + options: TargetOptions { + features: "+v8a,+strict-align,+neon,+fp-armv8".into(), + max_atomic_width: Some(128), + ..super::hermit_base::opts() + }, } } diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index 5632bcfcefe..446efa90d09 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -5,7 +5,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armebv7r-unknown-none-eabi".into(), + llvm_target: "armebv7r-none-eabi".into(), pointer_width: 32, data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), @@ -18,7 +18,7 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index 2815de3589d..0c9e99ff84b 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -5,7 +5,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armebv7r-unknown-none-eabihf".into(), + llvm_target: "armebv7r-none-eabihf".into(), pointer_width: 32, data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), @@ -19,7 +19,7 @@ pub fn target() -> Target { features: "+vfp3,-d32,-fp16".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armv4t_none_eabi.rs b/compiler/rustc_target/src/spec/armv4t_none_eabi.rs index d0f988b278f..44fdd3178af 100644 --- a/compiler/rustc_target/src/spec/armv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv4t_none_eabi.rs @@ -39,9 +39,9 @@ pub fn target() -> Target { has_thumb_interworking: true, relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - // from thumb_base, rust-lang/rust#44993. + // From thumb_base, rust-lang/rust#44993. emit_debug_gdb_scripts: false, - // from thumb_base, apparently gcc/clang give enums a minimum of 8 bits on no-os targets + // From thumb_base, GCC gives enums a minimum of 8 bits on no-os targets. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index 8cdf3c36ba2..c134f3e0907 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -18,7 +18,7 @@ pub fn target() -> Target { max_atomic_width: Some(64), panic_strategy: PanicStrategy::Abort, emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }; diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index 74905ed5a4e..68b2527985a 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -4,7 +4,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armv7r-unknown-none-eabi".into(), + llvm_target: "armv7r-none-eabi".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), @@ -17,7 +17,7 @@ pub fn target() -> Target { panic_strategy: PanicStrategy::Abort, max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index 516b3f5c17e..909765a310f 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -4,7 +4,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, Targ pub fn target() -> Target { Target { - llvm_target: "armv7r-unknown-none-eabihf".into(), + llvm_target: "armv7r-none-eabihf".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), @@ -18,7 +18,7 @@ pub fn target() -> Target { features: "+vfp3,-d32,-fp16".into(), max_atomic_width: Some(64), emit_debug_gdb_scripts: false, - // GCC and Clang default to 8 for arm-none here + // GCC defaults to 8 for arm-none here. c_enum_min_bits: Some(8), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index dd9991381e7..c6e98fc1a11 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -1,21 +1,15 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, TargetOptions, TlsModel}; pub fn opts() -> TargetOptions { - let pre_link_args = TargetOptions::link_args( - LinkerFlavor::Gnu(Cc::No, Lld::No), - &["--build-id", "--hash-style=gnu", "--Bstatic"], - ); - TargetOptions { os: "hermit".into(), - linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), - has_thread_local: true, - pre_link_args, - panic_strategy: PanicStrategy::Abort, + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + tls_model: TlsModel::InitialExec, position_independent_executables: true, static_position_independent_executables: true, - tls_model: TlsModel::InitialExec, + has_thread_local: true, + panic_strategy: PanicStrategy::Abort, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs index 1e066b271e2..983a449b006 100644 --- a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { llvm_target: "mipsisa32r6-unknown-linux-gnu".into(), pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), - arch: "mips".into(), + arch: "mips32r6".into(), options: TargetOptions { endian: Endian::Big, cpu: "mips32r6".into(), diff --git a/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs index 4785929c100..ec0facdfb7b 100644 --- a/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { llvm_target: "mipsisa32r6el-unknown-linux-gnu".into(), pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".into(), - arch: "mips".into(), + arch: "mips32r6".into(), options: TargetOptions { cpu: "mips32r6".into(), diff --git a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs index 766ac768064..16dd1c416f4 100644 --- a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -6,7 +6,7 @@ pub fn target() -> Target { llvm_target: "mipsisa64r6-unknown-linux-gnuabi64".into(), pointer_width: 64, data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(), - arch: "mips64".into(), + arch: "mips64r6".into(), options: TargetOptions { abi: "abi64".into(), endian: Endian::Big, diff --git a/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs index d2b07c654dc..8d0a6aa8f51 100644 --- a/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs @@ -5,7 +5,7 @@ pub fn target() -> Target { llvm_target: "mipsisa64r6el-unknown-linux-gnuabi64".into(), pointer_width: 64, data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".into(), - arch: "mips64".into(), + arch: "mips64r6".into(), options: TargetOptions { abi: "abi64".into(), // NOTE(mips64r6) matches C toolchain diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 6ae07f45f4a..9e556c6cb96 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -85,6 +85,7 @@ mod solaris_base; mod solid_base; mod thumb_base; mod uefi_msvc_base; +mod unikraft_linux_musl_base; mod vxworks_base; mod wasm_base; mod windows_gnu_base; @@ -1299,6 +1300,7 @@ supported_targets! { ("armv7-linux-androideabi", armv7_linux_androideabi), ("thumbv7neon-linux-androideabi", thumbv7neon_linux_androideabi), ("aarch64-linux-android", aarch64_linux_android), + ("riscv64-linux-android", riscv64_linux_android), ("aarch64-unknown-freebsd", aarch64_unknown_freebsd), ("armv6-unknown-freebsd", armv6_unknown_freebsd), @@ -1418,8 +1420,11 @@ supported_targets! { ("msp430-none-elf", msp430_none_elf), ("aarch64-unknown-hermit", aarch64_unknown_hermit), + ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl), + ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), diff --git a/compiler/rustc_target/src/spec/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/riscv64_linux_android.rs new file mode 100644 index 00000000000..af0d6855494 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64_linux_android.rs @@ -0,0 +1,19 @@ +use crate::spec::{CodeModel, SanitizerSet, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "riscv64-linux-android".into(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + arch: "riscv64".into(), + options: TargetOptions { + code_model: Some(CodeModel::Medium), + cpu: "generic-rv64".into(), + features: "+m,+a,+f,+d,+c".into(), + llvm_abiname: "lp64d".into(), + supported_sanitizers: SanitizerSet::ADDRESS, + max_atomic_width: Some(64), + ..super::android_base::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs new file mode 100644 index 00000000000..1f6a34c0cac --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_hermit.rs @@ -0,0 +1,20 @@ +use crate::spec::{CodeModel, RelocModel, Target, TargetOptions, TlsModel}; + +pub fn target() -> Target { + Target { + llvm_target: "riscv64-unknown-hermit".into(), + pointer_width: 64, + arch: "riscv64".into(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + options: TargetOptions { + cpu: "generic-rv64".into(), + features: "+m,+a,+f,+d,+c".into(), + relocation_model: RelocModel::Pic, + code_model: Some(CodeModel::Medium), + tls_model: TlsModel::LocalExec, + max_atomic_width: Some(64), + llvm_abiname: "lp64d".into(), + ..super::hermit_base::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index 2220b9326c9..0decfecb4cd 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -52,7 +52,7 @@ pub fn opts() -> TargetOptions { // breaks debugging. Preserve LR by default to prevent that from happening. frame_pointer: FramePointer::Always, // ARM supports multiple ABIs for enums, the linux one matches the default of 32 here - // but any arm-none or thumb-none target will be defaulted to 8 on GCC and clang + // but any arm-none or thumb-none target will be defaulted to 8 on GCC. c_enum_min_bits: Some(8), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index 9c59bb9114e..88a76f49acd 100644 --- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -45,8 +45,6 @@ pub fn target() -> Target { relocation_model: RelocModel::Static, // suggested from thumb_base, rust-lang/rust#44993. emit_debug_gdb_scripts: false, - // suggested from thumb_base, with no-os gcc/clang use 8-bit enums - c_enum_min_bits: Some(8), frame_pointer: FramePointer::MayOmit, main_needs_argc_argv: false, diff --git a/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs b/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs new file mode 100644 index 00000000000..9ccd0a1e7ca --- /dev/null +++ b/compiler/rustc_target/src/spec/unikraft_linux_musl_base.rs @@ -0,0 +1,15 @@ +use crate::spec::{cvs, PanicStrategy, RelocModel, TargetOptions}; + +pub fn opts() -> TargetOptions { + TargetOptions { + os: "linux".into(), + env: "musl".into(), + vendor: "unikraft".into(), + linker: Some("kraftld".into()), + relocation_model: RelocModel::Static, + families: cvs!["unix"], + has_thread_local: true, + panic_strategy: PanicStrategy::Abort, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs new file mode 100644 index 00000000000..2aa093b131f --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unikraft_linux_musl.rs @@ -0,0 +1,19 @@ +use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "x86_64-unknown-linux-musl".into(), + pointer_width: 64, + arch: "x86_64".into(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .into(), + options: TargetOptions { + cpu: "x86-64".into(), + plt_by_default: false, + pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]), + max_atomic_width: Some(64), + stack_probes: StackProbeType::X86, + ..super::unikraft_linux_musl_base::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs index 74ef2527c36..1eb06930111 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs @@ -1,19 +1,19 @@ -use crate::spec::{StackProbeType, Target}; +use crate::spec::{StackProbeType, Target, TargetOptions}; pub fn target() -> Target { - let mut base = super::hermit_base::opts(); - base.cpu = "x86-64".into(); - base.plt_by_default = false; - base.max_atomic_width = Some(64); - base.features = "+rdrnd,+rdseed".into(); - base.stack_probes = StackProbeType::X86; - Target { llvm_target: "x86_64-unknown-hermit".into(), pointer_width: 64, + arch: "x86_64".into(), data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .into(), - arch: "x86_64".into(), - options: base, + options: TargetOptions { + cpu: "x86-64".into(), + features: "+rdrnd,+rdseed".into(), + plt_by_default: false, + max_atomic_width: Some(64), + stack_probes: StackProbeType::X86, + ..super::hermit_base::opts() + }, } } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 217ba71b631..f57f1bad15d 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -1,3 +1,13 @@ +trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len -> + [one] argument + *[other] arguments + } + +trait_selection_adjust_signature_remove_borrow = consider adjusting the signature so it does not borrow its {$len -> + [one] argument + *[other] arguments + } + trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries} trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 54e22cc3d7f..dde9e9c9ac6 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,5 +1,8 @@ use crate::fluent_generated as fluent; -use rustc_errors::{ErrorGuaranteed, Handler, IntoDiagnostic}; +use rustc_errors::{ + AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, + SubdiagnosticMessage, +}; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, PolyTraitRef, Ty}; use rustc_span::{Span, Symbol}; @@ -97,3 +100,34 @@ pub struct InherentProjectionNormalizationOverflow { pub span: Span, pub ty: String, } + +pub enum AdjustSignatureBorrow { + Borrow { to_borrow: Vec<(Span, String)> }, + RemoveBorrow { remove_borrow: Vec<(Span, String)> }, +} + +impl AddToDiagnostic for AdjustSignatureBorrow { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + match self { + AdjustSignatureBorrow::Borrow { to_borrow } => { + diag.set_arg("len", to_borrow.len()); + diag.multipart_suggestion_verbose( + fluent::trait_selection_adjust_signature_borrow, + to_borrow, + Applicability::MaybeIncorrect, + ); + } + AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => { + diag.set_arg("len", remove_borrow.len()); + diag.multipart_suggestion_verbose( + fluent::trait_selection_adjust_signature_remove_borrow, + remove_borrow, + Applicability::MaybeIncorrect, + ); + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 312bd38178f..6efc1e7302c 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,5 +1,5 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, ObligationCtxt}; +use crate::traits::{self, DefiningAnchor, ObligationCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; @@ -80,7 +80,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { pub trait InferCtxtBuilderExt<'tcx> { fn enter_canonical_trait_query<K, R>( - &mut self, + self, canonical_key: &Canonical<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> @@ -108,7 +108,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { /// have `'tcx` be free on this function so that we can talk about /// `K: TypeFoldable<TyCtxt<'tcx>>`.) fn enter_canonical_trait_query<K, R>( - &mut self, + self, canonical_key: &Canonical<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> @@ -117,8 +117,9 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { R: Debug + TypeFoldable<TyCtxt<'tcx>>, Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { - let (infcx, key, canonical_inference_vars) = - self.build_with_canonical(DUMMY_SP, canonical_key); + let (infcx, key, canonical_inference_vars) = self + .with_opaque_type_inference(DefiningAnchor::Bubble) + .build_with_canonical(DUMMY_SP, canonical_key); let ocx = ObligationCtxt::new(&infcx); let value = operation(&ocx, key)?; ocx.make_canonicalized_query_response(canonical_inference_vars, value) diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 73362d82306..1b4af95cb8a 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -1,3 +1,16 @@ +//! Implements the `AliasRelate` goal, which is used when unifying aliases. +//! Doing this via a separate goal is called "deferred alias relation" and part +//! of our more general approach to "lazy normalization". +//! +//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches: +//! * normalizes-to: If `A` is a projection, we can prove the equivalent +//! projection predicate with B as the right-hand side of the projection. +//! This goal is computed in both directions, if both are aliases. +//! * subst-relate: Equate `A` and `B` by their substs, if they're both +//! aliases with the same def-id. +//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both +//! may apply, then we can compute the "intersection" of both normalizes-to by +//! performing them together. This is used specifically to resolve ambiguities. use super::{EvalCtxt, SolverMode}; use rustc_infer::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; @@ -66,24 +79,27 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Invert::Yes, )); // Relate via args - let subst_relate_response = self - .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction); - candidates.extend(subst_relate_response); + candidates.extend( + self.assemble_subst_relate_candidate( + param_env, alias_lhs, alias_rhs, direction, + ), + ); debug!(?candidates); if let Some(merged) = self.try_merge_responses(&candidates) { Ok(merged) } else { - // When relating two aliases and we have ambiguity, we prefer - // relating the generic arguments of the aliases over normalizing - // them. This is necessary for inference during typeck. + // When relating two aliases and we have ambiguity, if both + // aliases can be normalized to something, we prefer + // "bidirectionally normalizing" both of them within the same + // candidate. + // + // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/25>. // // As this is incomplete, we must not do so during coherence. match self.solver_mode() { SolverMode::Normal => { - if let Ok(subst_relate_response) = subst_relate_response { - Ok(subst_relate_response) - } else if let Ok(bidirectional_normalizes_to_response) = self + if let Ok(bidirectional_normalizes_to_response) = self .assemble_bidirectional_normalizes_to_candidate( param_env, lhs, rhs, direction, ) @@ -115,6 +131,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + // Computes the normalizes-to branch, with side-effects. This must be performed + // in a probe in order to not taint the evaluation context. fn normalizes_to_inner( &mut self, param_env: ty::ParamEnv<'tcx>, @@ -124,9 +142,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { invert: Invert, ) -> Result<(), NoSolution> { let other = match direction { - // This is purely an optimization. + // This is purely an optimization. No need to instantiate a new + // infer var and equate the RHS to it. ty::AliasRelationDirection::Equate => other, + // Instantiate an infer var and subtype our RHS to it, so that we + // properly represent a subtype relation between the LHS and RHS + // of the goal. ty::AliasRelationDirection::Subtype => { let fresh = self.next_term_infer_of_kind(other); let (sub, sup) = match invert { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 1e798998895..5ec48c7ac57 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -3,16 +3,17 @@ use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use crate::traits::coherence; -use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::elaborate; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; -use rustc_middle::ty::fast_reject::TreatProjections; -use rustc_middle::ty::TypeFoldable; +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{fast_reject, TypeFoldable}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; +use rustc_span::ErrorGuaranteed; use std::fmt::Debug; pub(super) mod structural_traits; @@ -87,16 +88,6 @@ pub(super) enum CandidateSource { AliasBound, } -/// Records additional information about what kind of built-in impl this is. -/// This should only be used by selection. -#[derive(Debug, Clone, Copy)] -pub(super) enum BuiltinImplSource { - TraitUpcasting, - Object, - Misc, - Ambiguity, -} - /// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display @@ -109,10 +100,10 @@ pub(super) trait GoalKind<'tcx>: fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; - // Try equating an assumption predicate against a goal's predicate. If it - // holds, then execute the `then` callback, which should do any additional - // work, then produce a response (typically by executing - // [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). + /// Try equating an assumption predicate against a goal's predicate. If it + /// holds, then execute the `then` callback, which should do any additional + /// work, then produce a response (typically by executing + /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -120,9 +111,9 @@ pub(super) trait GoalKind<'tcx>: then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx>; - // Consider a clause, which consists of a "assumption" and some "requirements", - // to satisfy a goal. If the requirements hold, then attempt to satisfy our - // goal by equating it with the assumption. + /// Consider a clause, which consists of a "assumption" and some "requirements", + /// to satisfy a goal. If the requirements hold, then attempt to satisfy our + /// goal by equating it with the assumption. fn consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -149,9 +140,9 @@ pub(super) trait GoalKind<'tcx>: }) } - // Consider a clause specifically for a `dyn Trait` self type. This requires - // additionally checking all of the supertraits and object bounds to hold, - // since they're not implied by the well-formedness of the object type. + /// Consider a clause specifically for a `dyn Trait` self type. This requires + /// additionally checking all of the supertraits and object bounds to hold, + /// since they're not implied by the well-formedness of the object type. fn consider_object_bound_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -182,101 +173,103 @@ pub(super) trait GoalKind<'tcx>: impl_def_id: DefId, ) -> QueryResult<'tcx>; - // A type implements an `auto trait` if its components do as well. These components - // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`]. + /// If the predicate contained an error, we want to avoid emitting unnecessary trait + /// errors but still want to emit errors for other trait goals. We have some special + /// handling for this case. + /// + /// Trait goals always hold while projection goals never do. This is a bit arbitrary + /// but prevents incorrect normalization while hiding any trait errors. + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + guar: ErrorGuaranteed, + ) -> QueryResult<'tcx>; + + /// A type implements an `auto trait` if its components do as well. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`]. fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A trait alias holds if the RHS traits and `where` clauses hold. + /// A trait alias holds if the RHS traits and `where` clauses hold. fn consider_trait_alias_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is `Copy` or `Clone` if its components are `Sized`. These components - // are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`]. + /// A type is `Copy` or `Clone` if its components are `Sized`. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These - // components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`]. + /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`]. fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is `PointerLike` if we can compute its layout, and that layout - // matches the layout of `usize`. + /// A type is `PointerLike` if we can compute its layout, and that layout + /// matches the layout of `usize`. fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A type is a `FnPtr` if it is of `FnPtr` type. + /// A type is a `FnPtr` if it is of `FnPtr` type. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>` - // family of traits where `A` is given by the signature of the type. + /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>` + /// family of traits where `A` is given by the signature of the type. fn consider_builtin_fn_trait_candidates( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, kind: ty::ClosureKind, ) -> QueryResult<'tcx>; - // `Tuple` is implemented if the `Self` type is a tuple. + /// `Tuple` is implemented if the `Self` type is a tuple. fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // `Pointee` is always implemented. - // - // See the projection implementation for the `Metadata` types for all of - // the built-in types. For structs, the metadata type is given by the struct - // tail. + /// `Pointee` is always implemented. + /// + /// See the projection implementation for the `Metadata` types for all of + /// the built-in types. For structs, the metadata type is given by the struct + /// tail. fn consider_builtin_pointee_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A generator (that comes from an `async` desugaring) is known to implement - // `Future<Output = O>`, where `O` is given by the generator's return type - // that was computed during type-checking. + /// A generator (that comes from an `async` desugaring) is known to implement + /// `Future<Output = O>`, where `O` is given by the generator's return type + /// that was computed during type-checking. fn consider_builtin_future_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // A generator (that doesn't come from an `async` desugaring) is known to - // implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield, - // and return types of the generator computed during type-checking. + /// A generator (that doesn't come from an `async` desugaring) is known to + /// implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield, + /// and return types of the generator computed during type-checking. fn consider_builtin_generator_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; - // The most common forms of unsizing are array to slice, and concrete (Sized) - // type into a `dyn Trait`. ADTs and Tuples can also have their final field - // unsized if it's generic. - fn consider_builtin_unsize_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx>; - - // `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or - // if `Trait2` is a (transitive) supertrait of `Trait2`. - fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec<CanonicalResponse<'tcx>>; - fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -291,6 +284,25 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + + /// Consider (possibly several) candidates to upcast or unsize a type to another + /// type. + /// + /// The most common forms of unsizing are array to slice, and concrete (Sized) + /// type into a `dyn Trait`. ADTs and Tuples can also have their final field + /// unsized if it's generic. + /// + /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or + /// if `Trait2` is a (transitive) supertrait of `Trait2`. + /// + /// We return the `BuiltinImplSource` for each candidate as it is needed + /// for unsize coercion in hir typeck and because it is difficult to + /// otherwise recompute this for codegen. This is a bit of a mess but the + /// easiest way to maintain the existing behavior for now. + fn consider_builtin_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; } impl<'tcx> EvalCtxt<'_, 'tcx> { @@ -299,35 +311,66 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, ) -> Vec<Candidate<'tcx>> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); + if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { + return ambig; + } + + let mut candidates = self.assemble_candidates_via_self_ty(goal); + + self.assemble_blanket_impl_candidates(goal, &mut candidates); + + self.assemble_param_env_candidates(goal, &mut candidates); + + candidates + } - // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule, - // object bound, alias bound, etc. We are unable to determine this until we can at - // least structurally resolve the type one layer. - if goal.predicate.self_ty().is_ty_var() { - return vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + /// `?0: Trait` is ambiguous, because it may be satisfied via a builtin rule, + /// object bound, alias bound, etc. We are unable to determine this until we can at + /// least structurally resolve the type one layer. + /// + /// It would also require us to consider all impls of the trait, which is both pretty + /// bad for perf and would also constrain the self type if there is just a single impl. + fn assemble_self_ty_infer_ambiguity_response<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + ) -> Option<Vec<Candidate<'tcx>>> { + goal.predicate.self_ty().is_ty_var().then(|| { + vec![Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result: self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .unwrap(), - }]; + }] + }) + } + + /// Assemble candidates which apply to the self type. This only looks at candidate which + /// apply to the specific self type and ignores all others. + /// + /// Returns `None` if the self type is still ambiguous. + fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + ) -> Vec<Candidate<'tcx>> { + debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); + if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { + return ambig; } let mut candidates = Vec::new(); - self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); - - self.assemble_impl_candidates(goal, &mut candidates); + self.assemble_non_blanket_impl_candidates(goal, &mut candidates); self.assemble_builtin_impl_candidates(goal, &mut candidates); - self.assemble_param_env_candidates(goal, &mut candidates); - self.assemble_alias_bound_candidates(goal, &mut candidates); self.assemble_object_bound_candidates(goal, &mut candidates); self.assemble_coherence_unknowable_candidates(goal, &mut candidates); + self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); + candidates } @@ -359,10 +402,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.with_incremented_depth( |ecx| { let result = ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::Maybe(MaybeCause::Overflow), + Certainty::OVERFLOW, )?; Ok(vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result, }]) }, @@ -385,7 +428,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // have a `Normalized` candidate. This doesn't work as long as we // use `CandidateSource` in winnowing. let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - Ok(ecx.assemble_and_evaluate_candidates(goal)) + Ok(ecx.assemble_candidates_via_self_ty(goal)) }, ) }); @@ -396,22 +439,125 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn assemble_impl_candidates<G: GoalKind<'tcx>>( + fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, candidates: &mut Vec<Candidate<'tcx>>, ) { let tcx = self.tcx(); - tcx.for_each_relevant_impl_treating_projections( - goal.predicate.trait_def_id(tcx), - goal.predicate.self_ty(), - TreatProjections::NextSolverLookup, - |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) { + let self_ty = goal.predicate.self_ty(); + let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); + let mut consider_impls_for_simplified_type = |simp| { + if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { + for &impl_def_id in impls_for_type { + match G::consider_impl_candidate(self, goal, impl_def_id) { + Ok(result) => candidates + .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Err(NoSolution) => (), + } + } + } + }; + + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::Never + | ty::Tuple(_) => { + let simp = + fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap(); + consider_impls_for_simplified_type(simp); + } + + // HACK: For integer and float variables we have to manually look at all impls + // which have some integer or float as a self type. + ty::Infer(ty::IntVar(_)) => { + use ty::IntTy::*; + use ty::UintTy::*; + // This causes a compiler error if any new integer kinds are added. + let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; + let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; + let possible_integers = [ + // signed integers + SimplifiedType::Int(I8), + SimplifiedType::Int(I16), + SimplifiedType::Int(I32), + SimplifiedType::Int(I64), + SimplifiedType::Int(I128), + SimplifiedType::Int(Isize), + // unsigned integers + SimplifiedType::Uint(U8), + SimplifiedType::Uint(U16), + SimplifiedType::Uint(U32), + SimplifiedType::Uint(U64), + SimplifiedType::Uint(U128), + SimplifiedType::Uint(Usize), + ]; + for simp in possible_integers { + consider_impls_for_simplified_type(simp); + } + } + + ty::Infer(ty::FloatVar(_)) => { + // This causes a compiler error if any new float kinds are added. + let (ty::FloatTy::F32 | ty::FloatTy::F64); + let possible_floats = [ + SimplifiedType::Float(ty::FloatTy::F32), + SimplifiedType::Float(ty::FloatTy::F64), + ]; + + for simp in possible_floats { + consider_impls_for_simplified_type(simp); + } + } + + // The only traits applying to aliases and placeholders are blanket impls. + // + // Impls which apply to an alias after normalization are handled by + // `assemble_candidates_after_normalizing_self_ty`. + ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), + + // FIXME: These should ideally not exist as a self type. It would be nice for + // the builtin auto trait impls of generators should instead directly recurse + // into the witness. + ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (), + + // These variants should not exist as a self type. + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Param(_) + | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), + } + } + + fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let tcx = self.tcx(); + let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); + for &impl_def_id in trait_impls.blanket_impls() { + match G::consider_impl_candidate(self, goal, impl_def_id) { Ok(result) => candidates .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), Err(NoSolution) => (), - }, - ); + } + } } #[instrument(level = "debug", skip_all)] @@ -420,8 +566,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec<Candidate<'tcx>>, ) { - let lang_items = self.tcx().lang_items(); - let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + let tcx = self.tcx(); + let lang_items = tcx.lang_items(); + let trait_def_id = goal.predicate.trait_def_id(tcx); // N.B. When assembling built-in candidates for lang items that are also // `auto` traits, then the auto trait candidate that is assembled in @@ -430,9 +577,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Instead of adding the logic here, it's a better idea to add it in // `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in // `solve::trait_goals` instead. - let result = if self.tcx().trait_is_auto(trait_def_id) { + let result = if let Err(guar) = goal.predicate.error_reported() { + G::consider_error_guaranteed_candidate(self, guar) + } else if tcx.trait_is_auto(trait_def_id) { G::consider_auto_trait_candidate(self, goal) - } else if self.tcx().trait_is_alias(trait_def_id) { + } else if tcx.trait_is_alias(trait_def_id) { G::consider_trait_alias_candidate(self, goal) } else if lang_items.sized_trait() == Some(trait_def_id) { G::consider_builtin_sized_candidate(self, goal) @@ -454,8 +603,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_future_candidate(self, goal) } else if lang_items.gen_trait() == Some(trait_def_id) { G::consider_builtin_generator_candidate(self, goal) - } else if lang_items.unsize_trait() == Some(trait_def_id) { - G::consider_builtin_unsize_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) } else if lang_items.destruct_trait() == Some(trait_def_id) { @@ -477,11 +624,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // There may be multiple unsize candidates for a trait with several supertraits: // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` if lang_items.unsize_trait() == Some(trait_def_id) { - for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { - candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), - result, - }); + for (result, source) in G::consider_builtin_unsize_candidates(self, goal) { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); } } } @@ -697,29 +841,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(bounds, ..) => bounds, }; - let own_bounds: FxIndexSet<_> = - bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect(); - for assumption in elaborate(tcx, own_bounds.iter().copied()) - // we only care about bounds that match the `Self` type - .filter_only_self() - { - // FIXME: Predicates are fully elaborated in the object type's existential bounds - // list. We want to only consider these pre-elaborated projections, and not other - // projection predicates that we reach by elaborating the principal trait ref, - // since that'll cause ambiguity. - // - // We can remove this when we have implemented lifetime intersections in responses. - if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) { - continue; + // Consider all of the auto-trait and projection bounds, which don't + // need to be recorded as a `BuiltinImplSource::Object` since they don't + // really have a vtable base... + for bound in bounds { + match bound.skip_binder() { + ty::ExistentialPredicate::Trait(_) => { + // Skip principal + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => { + match G::consider_object_bound_candidate( + self, + goal, + bound.with_self_ty(tcx, self_ty), + ) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + } } + } - match G::consider_object_bound_candidate(self, goal, assumption) { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object), - result, - }), - Err(NoSolution) => (), - } + // FIXME: We only need to do *any* of this if we're considering a trait goal, + // since we don't need to look at any supertrait or anything if we are doing + // a projection goal. + if let Some(principal) = bounds.principal() { + let principal_trait_ref = principal.with_self_ty(tcx, self_ty); + self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| { + match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object { + vtable_base, + }), + result, + }), + Err(NoSolution) => (), + } + }); } } @@ -739,7 +901,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), result, }), // FIXME: This will be reachable at some point if we're in diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index a8ba98bef6d..a2db35e069e 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -1,3 +1,5 @@ +//! Code which is used by built-in goals that match "structurally", such a auto +//! traits, `Copy`/`Clone`. use rustc_data_structures::fx::FxHashMap; use rustc_hir::{def_id::DefId, Movability, Mutability}; use rustc_infer::traits::query::NoSolution; diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index 255620489ff..88771f90756 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -125,9 +125,8 @@ impl<'a, 'tcx> Canonicalizer<'a, 'tcx> { // - var_infos: [E0, U1, E1, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 6 // - var_infos: [E0, U1, E1, U1, E1, E2, U2], curr_compressed_uv: 2, next_orig_uv: - // - // This algorithm runs in `O(nm)` where `n` is the number of different universe - // indices in the input and `m` is the number of canonical variables. - // This should be fine as both `n` and `m` are expected to be small. + // This algorithm runs in `O(n²)` where `n` is the number of different universe + // indices in the input. This should be fine as `n` is expected to be small. let mut curr_compressed_uv = ty::UniverseIndex::ROOT; let mut existential_in_new_uv = false; let mut next_orig_uv = Some(ty::UniverseIndex::ROOT); @@ -263,14 +262,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { ty::ReError(_) => return r, }; - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == r.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(r.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); + let existing_bound_var = match self.canonicalize_mode { + CanonicalizeMode::Input => None, + CanonicalizeMode::Response { .. } => { + self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from) + } + }; + let var = existing_bound_var.unwrap_or_else(|| { + let var = ty::BoundVar::from(self.variables.len()); + self.variables.push(r.into()); + self.primitive_var_infos.push(CanonicalVarInfo { kind }); + var + }); let br = ty::BoundRegion { var, kind: BrAnon(None) }; ty::Region::new_late_bound(self.interner(), self.binder_index, br) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 6e0aa08c307..485bc912c44 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -11,8 +11,8 @@ use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ - CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause, - PredefinedOpaques, PredefinedOpaquesData, QueryResult, + CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, + PredefinedOpaquesData, QueryResult, }; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ @@ -25,6 +25,7 @@ use std::io::Write; use std::ops::ControlFlow; use crate::traits::specialization_graph; +use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; use super::search_graph::{self, OverflowHandler}; @@ -271,6 +272,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // assertions against dropping an `InferCtxt` without taking opaques. // FIXME: Once we remove support for the old impl we can remove this. if input.anchor != DefiningAnchor::Error { + // This seems ok, but fragile. let _ = infcx.take_opaque_types(); } @@ -342,7 +344,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { Ok(response) => response, }; - let has_changed = !canonical_response.value.var_values.is_identity() + let has_changed = !canonical_response.value.var_values.is_identity_modulo_regions() || !canonical_response.value.external_constraints.opaque_types.is_empty(); let (certainty, nested_goals) = match self.instantiate_and_apply_query_response( goal.param_env, @@ -473,7 +475,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut new_goals = NestedGoals::new(); let response = self.repeat_while_none( - |_| Ok(Certainty::Maybe(MaybeCause::Overflow)), + |_| Ok(Certainty::OVERFLOW), |this| { this.inspect.evaluate_added_goals_loop_start(); @@ -920,4 +922,39 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(ErrorHandled::TooGeneric) => None, } } + + /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor` + /// for every trait ref encountered (including the principal). Passes both the vtable + /// base and the (optional) vptr slot. + pub(super) fn walk_vtable( + &mut self, + principal: ty::PolyTraitRef<'tcx>, + mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>), + ) { + let tcx = self.tcx(); + let mut offset = 0; + prepare_vtable_segments::<()>(tcx, principal, |segment| { + match segment { + VtblSegment::MetadataDSA => { + offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let own_vtable_entries = count_own_vtable_entries(tcx, trait_ref); + + supertrait_visitor( + self, + trait_ref, + offset, + emit_vptr.then(|| offset + own_vtable_entries), + ); + + offset += own_vtable_entries; + if emit_vptr { + offset += 1; + } + } + } + ControlFlow::Continue(()) + }); + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 9f58e66d04e..7323b98b8ce 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -1,13 +1,13 @@ -/// Canonicalization is used to separate some goal from its context, -/// throwing away unnecessary information in the process. -/// -/// This is necessary to cache goals containing inference variables -/// and placeholders without restricting them to the current `InferCtxt`. -/// -/// Canonicalization is fairly involved, for more details see the relevant -/// section of the [rustc-dev-guide][c]. -/// -/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html +//! Canonicalization is used to separate some goal from its context, +//! throwing away unnecessary information in the process. +//! +//! This is necessary to cache goals containing inference variables +//! and placeholders without restricting them to the current `InferCtxt`. +//! +//! Canonicalization is fairly involved, for more details see the relevant +//! section of the [rustc-dev-guide][c]. +//! +//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::{CanonicalResponse, QueryResult, Response}; @@ -135,6 +135,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) } + /// Computes the region constraints and *new* opaque types registered when + /// proving a goal. + /// + /// If an opaque was already constrained before proving this goal, then the + /// external constraints do not need to record that opaque, since if it is + /// further constrained by inference, that will be passed back in the var + /// values. #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> { // We only check for leaks from universes which were entered inside diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index a6d118d8cc2..1aef9a885bc 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -1,25 +1,20 @@ -use std::ops::ControlFlow; - use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; -use rustc_infer::traits::util::supertraits; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt}; use rustc_infer::traits::{ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, }; use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; use rustc_middle::traits::{ - ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, - ObligationCause, SelectionError, + BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError, }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::DUMMY_SP; -use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; +use crate::solve::assembly::{Candidate, CandidateSource}; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; use crate::solve::search_graph::OverflowHandler; -use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use crate::traits::StructurallyNormalizeExt; use crate::traits::TraitEngineExt; @@ -105,38 +100,34 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { rematch_impl(self, goal, def_id, nested_obligations) } - // Rematching the dyn upcast or object goal will instantiate the same nested - // goals that would have caused the ambiguity, so we can still make progress here - // regardless. - // FIXME: This doesn't actually check the object bounds hold here. - ( - _, - CandidateSource::BuiltinImpl( - BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting, - ), - ) => rematch_object(self, goal, nested_obligations), - - (Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) + // If an unsize goal is ambiguous, then we can manually rematch it to make + // selection progress for coercion during HIR typeck. If it is *not* ambiguous, + // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals, + // and we need to rematch those to detect tuple unsizing and trait upcasting. + // FIXME: This will be wrong if we have param-env or where-clause bounds + // with the unsize goal -- we may need to mark those with different impl + // sources. + (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src)) + | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc)) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => { - rematch_unsize(self, goal, nested_obligations) + rematch_unsize(self, goal, nested_obligations, src, certainty) } // Technically some builtin impls have nested obligations, but if // `Certainty::Yes`, then they should've all been verified and don't // need re-checking. - (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { - Ok(Some(ImplSource::Builtin(nested_obligations))) + (Certainty::Yes, CandidateSource::BuiltinImpl(src)) => { + Ok(Some(ImplSource::Builtin(src, nested_obligations))) } // It's fine not to do anything to rematch these, since there are no // nested obligations. (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { - Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) + Ok(Some(ImplSource::Param(ty::BoundConstness::NotConst, nested_obligations))) } - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity)) - | (Certainty::Maybe(_), _) => Ok(None), + (Certainty::Maybe(_), _) => Ok(None), } } } @@ -183,11 +174,12 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( } (_, CandidateSource::ParamEnv(_)) => true, + // FIXME: we could prefer earlier vtable bases perhaps... ( - CandidateSource::BuiltinImpl(BuiltinImplSource::Object), - CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), ) => false, - (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true, + (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. })) => true, (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { tcx.specializes((other_def_id, victim_def_id)) @@ -225,102 +217,6 @@ fn rematch_impl<'tcx>( Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested }))) } -fn rematch_object<'tcx>( - infcx: &InferCtxt<'tcx>, - goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, - mut nested: Vec<PredicateObligation<'tcx>>, -) -> SelectionResult<'tcx, Selection<'tcx>> { - let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); - let ty::Dynamic(data, _, source_kind) = *a_ty.kind() else { bug!() }; - let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, a_ty); - - let (is_upcasting, target_trait_ref_unnormalized) = - if Some(goal.predicate.def_id()) == infcx.tcx.lang_items().unsize_trait() { - assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*"); - let b_ty = structurally_normalize( - goal.predicate.trait_ref.args.type_at(1), - infcx, - goal.param_env, - &mut nested, - ); - if let ty::Dynamic(data, _, ty::Dyn) = *b_ty.kind() { - // FIXME: We also need to ensure that the source lifetime outlives the - // target lifetime. This doesn't matter for codegen, though, and only - // *really* matters if the goal's certainty is ambiguous. - (true, data.principal().unwrap().with_self_ty(infcx.tcx, a_ty)) - } else { - bug!() - } - } else { - (false, ty::Binder::dummy(goal.predicate.trait_ref)) - }; - - let mut target_trait_ref = None; - for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) { - let result = infcx.commit_if_ok(|_| { - infcx.at(&ObligationCause::dummy(), goal.param_env).eq( - DefineOpaqueTypes::No, - target_trait_ref_unnormalized, - candidate_trait_ref, - ) - - // FIXME: We probably should at least shallowly verify these... - }); - - match result { - Ok(InferOk { value: (), obligations }) => { - target_trait_ref = Some(candidate_trait_ref); - nested.extend(obligations); - break; - } - Err(_) => continue, - } - } - - let target_trait_ref = target_trait_ref.unwrap(); - - let mut offset = 0; - let Some((vtable_base, vtable_vptr_slot)) = - prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| { - match segment { - VtblSegment::MetadataDSA => { - offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref); - - if trait_ref == target_trait_ref { - if emit_vptr { - return ControlFlow::Break(( - offset, - Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)), - )); - } else { - return ControlFlow::Break((offset, None)); - } - } - - offset += own_vtable_entries; - if emit_vptr { - offset += 1; - } - } - } - ControlFlow::Continue(()) - }) - else { - bug!(); - }; - - // If we're upcasting, get the offset of the vtable pointer, otherwise get - // the base of the vtable. - Ok(Some(if is_upcasting { - ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) - } else { - ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) - })) -} - /// The `Unsize` trait is particularly important to coercion, so we try rematch it. /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait /// goal assembly in the solver, both for soundness and in order to avoid ICEs. @@ -328,12 +224,24 @@ fn rematch_unsize<'tcx>( infcx: &InferCtxt<'tcx>, goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, mut nested: Vec<PredicateObligation<'tcx>>, + source: BuiltinImplSource, + certainty: Certainty, ) -> SelectionResult<'tcx, Selection<'tcx>> { let tcx = infcx.tcx; - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - + let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); + let b_ty = structurally_normalize( + goal.predicate.trait_ref.args.type_at(1), + infcx, + goal.param_env, + &mut nested, + ); match (a_ty.kind(), b_ty.kind()) { + // Stall any ambiguous upcasting goals, since we can't rematch those + (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty { + Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))), + _ => Ok(None), + }, + // `T` -> `dyn Trait` upcasting (_, &ty::Dynamic(data, region, ty::Dyn)) => { // Check that the type implements all of the predicates of the def-id. // (i.e. the principal, all of the associated types match, and any auto traits) @@ -360,6 +268,8 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), )); + + Ok(Some(ImplSource::Builtin(source, nested))) } // `[T; n]` -> `[T]` unsizing (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { @@ -370,6 +280,8 @@ fn rematch_unsize<'tcx>( .expect("expected rematch to succeed") .into_obligations(), ); + + Ok(Some(ImplSource::Builtin(source, nested))) } // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) @@ -420,6 +332,8 @@ fn rematch_unsize<'tcx>( goal.param_env, ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), )); + + Ok(Some(ImplSource::Builtin(source, nested))) } // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) @@ -439,21 +353,27 @@ fn rematch_unsize<'tcx>( .into_obligations(), ); - // Similar to ADTs, require that the rest of the fields are equal. + // Similar to ADTs, require that we can unsize the tail. nested.push(Obligation::new( tcx, ObligationCause::dummy(), goal.param_env, ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), )); + + // We need to be able to detect tuple unsizing to require its feature gate. + assert_eq!( + source, + BuiltinImplSource::TupleUnsizing, + "compiler-errors wants to know if this can ever be triggered..." + ); + Ok(Some(ImplSource::Builtin(source, nested))) + } + _ => { + assert_ne!(certainty, Certainty::Yes); + Ok(None) } - // FIXME: We *could* ICE here if either: - // 1. the certainty is `Certainty::Yes`, - // 2. we're in codegen (which should mean `Certainty::Yes`). - _ => return Ok(None), } - - Ok(Some(ImplSource::Builtin(nested))) } fn structurally_normalize<'tcx>( diff --git a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs index d10a14ff742..28fe59b7f6a 100644 --- a/compiler/rustc_trait_selection/src/solve/inherent_projection.rs +++ b/compiler/rustc_trait_selection/src/solve/inherent_projection.rs @@ -1,3 +1,9 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(inherent_associated_type)]`. Since astconv already determines +//! which impl the IAT is being projected from, we just: +//! 1. instantiate substs, +//! 2. equate the self type, and +//! 3. instantiate and register where clauses. use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 7c15c3c0e8b..63e48c94a86 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -1,14 +1,19 @@ -//! The new trait solver, currently still WIP. +//! The next-generation trait solver, currently still WIP. //! -//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to -//! interact with this solver. +//! As a user of rust, you can use `-Ztrait-solver=next` or `next-coherence` +//! to enable the new trait solver always, or just within coherence, respectively. +//! +//! As a developer of rustc, you shouldn't be using the new trait +//! solver without asking the trait-system-refactor-initiative, but it can +//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will +//! ensure that trait solving using that inference context will be routed +//! to the new trait solver. //! //! For a high-level overview of how this solver works, check out the relevant //! section of the rustc-dev-guide. //! //! FIXME(@lcnr): Write that section. If you read this before then ask me //! about it on zulip. - use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index f51f4edb933..091b7f33834 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -28,7 +28,8 @@ pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( /// its input to be already fully resolved. /// /// Additionally takes a list of universes which represents the binders which have been -/// entered before passing `value` to the function. +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( at: At<'_, 'tcx>, value: T, @@ -194,7 +195,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { mapped_regions, mapped_types, mapped_consts, - &mut self.universes, + &self.universes, result, )) } else { @@ -224,7 +225,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { mapped_regions, mapped_types, mapped_consts, - &mut self.universes, + &self.universes, result, )) } else { diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 2e537d1c358..f08adc0208b 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -1,3 +1,6 @@ +//! Computes a normalizes-to (projection) goal for opaque types. This goal +//! behaves differently depending on the param-env's reveal mode and whether +//! the opaque is in a defining scope. use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::traits::Reveal; diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d677fbdc7f4..2ce57751740 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -2,7 +2,6 @@ use crate::traits::specialization_graph; use super::assembly::{self, structural_traits}; use super::EvalCtxt; -use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -11,11 +10,12 @@ use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; -use rustc_span::{sym, DUMMY_SP}; +use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] @@ -246,6 +246,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }) } + /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error` + /// and succeed. Can experiment with this to figure out what results in better error messages. + fn consider_error_guaranteed_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _guar: ErrorGuaranteed, + ) -> QueryResult<'tcx> { + Err(NoSolution) + } + fn consider_auto_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -494,17 +503,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - bug!("`Unsize` does not have an associated type: {:?}", goal); - } - - fn consider_builtin_dyn_upcast_candidates( + fn consider_builtin_unsize_candidates( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - ) -> Vec<CanonicalResponse<'tcx>> { + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { bug!("`Unsize` does not have an associated type: {:?}", goal); } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index f00456e26df..c25d6eca918 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -9,7 +9,7 @@ use cache::ProvisionalCache; use overflow::OverflowData; use rustc_index::IndexVec; use rustc_middle::dep_graph::DepKind; -use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; @@ -58,10 +58,10 @@ impl<'tcx> SearchGraph<'tcx> { /// /// We could add another global cache for coherence instead, /// but that's effort so let's only do it if necessary. - pub(super) fn should_use_global_cache(&self) -> bool { + pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { match self.mode { - SolverMode::Normal => true, - SolverMode::Coherence => false, + SolverMode::Normal => &tcx.new_solver_evaluation_cache, + SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache, } } @@ -144,11 +144,7 @@ impl<'tcx> SearchGraph<'tcx> { { Err(cache.provisional_result(entry_index)) } else { - Err(super::response_no_constraints( - tcx, - input, - Certainty::Maybe(MaybeCause::Overflow), - )) + Err(super::response_no_constraints(tcx, input, Certainty::OVERFLOW)) } } } @@ -213,8 +209,8 @@ impl<'tcx> SearchGraph<'tcx> { inspect: &mut ProofTreeBuilder<'tcx>, mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { - if self.should_use_global_cache() && inspect.use_global_cache() { - if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) { + if inspect.use_global_cache() { + if let Some(result) = self.global_cache(tcx).get(&canonical_input, tcx) { debug!(?canonical_input, ?result, "cache hit"); inspect.cache_hit(CacheHit::Global); return result; @@ -278,13 +274,10 @@ impl<'tcx> SearchGraph<'tcx> { // dependencies, our non-root goal may no longer appear as child of the root goal. // // See https://github.com/rust-lang/rust/pull/108071 for some additional context. - let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty(); - if self.should_use_global_cache() && can_cache { - tcx.new_solver_evaluation_cache.insert( - current_goal.input, - dep_node, - current_goal.response, - ); + let can_cache = inspect.use_global_cache() + && (!self.overflow_data.did_overflow() || self.stack.is_empty()); + if can_cache { + self.global_cache(tcx).insert(current_goal.input, dep_node, current_goal.response) } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index e0a2e0c5cc2..83a051eeb1a 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -1,6 +1,6 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, MaybeCause, QueryResult}; +use rustc_middle::traits::solve::{Certainty, QueryResult}; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; @@ -115,6 +115,6 @@ impl<'tcx> SearchGraph<'tcx> { goal: Canonical<'tcx, impl Sized>, ) -> QueryResult<'tcx> { self.overflow_data.deal_with_overflow(); - response_no_constraints(tcx, goal, Certainty::Maybe(MaybeCause::Overflow)) + response_no_constraints(tcx, goal, Certainty::OVERFLOW) } } diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index e7867eead15..14a5b9656e5 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,17 +1,18 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; +use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; -use rustc_infer::traits::util::supertraits; +use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; -use rustc_middle::traits::Reveal; +use rustc_middle::traits::{BuiltinImplSource, Reveal}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; -use rustc_span::DUMMY_SP; +use rustc_span::{ErrorGuaranteed, DUMMY_SP}; impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn self_ty(self) -> Ty<'tcx> { @@ -78,6 +79,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + _guar: ErrorGuaranteed, + ) -> QueryResult<'tcx> { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -358,7 +366,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidate( + fn consider_builtin_discriminant_kind_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { @@ -366,130 +374,159 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - let tcx = ecx.tcx(); - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - if b_ty.is_ty_var() { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + // `DiscriminantKind` is automatically implemented for every type. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver + + // `Destruct` is automatically implemented for every type in + // non-const environments. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // `rustc_transmute` does not have support for type or const params + if goal.has_non_region_placeholders() { + return Err(NoSolution); + } + + // Erase regions because we compute layouts in `rustc_transmute`, + // which will ICE for region vars. + let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); + + let Some(assume) = + rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) + else { + return Err(NoSolution); + }; + + let certainty = ecx.is_transmutable( + rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, + args.type_at(2), + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + } + + fn consider_builtin_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return vec![]; } - ecx.probe_candidate("builtin unsize").enter(|ecx| { + + let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| { + ( + ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(), + BuiltinImplSource::Misc, + ) + }; + + let result_to_single = |result, source| match result { + Ok(resp) => vec![(resp, source)], + Err(NoSolution) => vec![], + }; + + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + // We need to normalize the b_ty since it's matched structurally + // in the other functions below. + let b_ty = match ecx + .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) + { + Ok(Some(b_ty)) => b_ty, + Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], + Err(_) => return vec![], + }; + + let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); match (a_ty.kind(), b_ty.kind()) { - // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` - (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { - // Dyn upcasting is handled separately, since due to upcasting, - // when there are two supertraits that differ by args, we - // may return more than one query response. - Err(NoSolution) - } - // `T` -> `dyn Trait` unsizing - (_, &ty::Dynamic(data, region, ty::Dyn)) => { - // Can only unsize to an object-safe type - if data - .principal_def_id() - .is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) - { - return Err(NoSolution); - } + (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"), + (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)], - let Some(sized_def_id) = tcx.lang_items().sized_trait() else { - return Err(NoSolution); - }; - // Check that the type implements all of the predicates of the def-id. - // (i.e. the principal, all of the associated types match, and any auto traits) - ecx.add_goals( - data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), - ); - // The type must be Sized to be unsized. - ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); - // The type must outlive the lifetime of the `dyn` we're unsizing into. - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // `[T; n]` -> `[T]` unsizing - (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { - // We just require that the element type stays the same - ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` + // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. + ( + &ty::Dynamic(a_data, a_region, ty::Dyn), + &ty::Dynamic(b_data, b_region, ty::Dyn), + ) => ecx.consider_builtin_dyn_upcast_candidates( + goal, a_data, a_region, b_data, b_region, + ), + + // `T` -> `dyn Trait` unsizing + (_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single( + ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region), + BuiltinImplSource::Misc, + ), + + // `[T; N]` -> `[T]` unsizing + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single( + ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty), + BuiltinImplSource::Misc, + ), + + // `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) - if a_def.is_struct() && a_def.did() == b_def.did() => + if a_def.is_struct() && a_def == b_def => { - let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); - // We must be unsizing some type parameters. This also implies - // that the struct has a tail field. - if unsizing_params.is_empty() { - return Err(NoSolution); - } - - let tail_field = a_def.non_enum_variant().tail(); - let tail_field_ty = tcx.type_of(tail_field.did); - - let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); - let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); - - // Substitute just the unsizing params from B into A. The type after - // this substitution must be equal to B. This is so we don't unsize - // unrelated type parameters. - let new_a_args = - tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| { - if unsizing_params.contains(i as u32) { b_args[i] } else { a } - })); - let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args); - - // Finally, we require that `TailA: Unsize<TailB>` for the tail field - // types. - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - ecx.add_goal(goal.with( - tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + result_to_single( + ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args), + BuiltinImplSource::Misc, + ) } - // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>` + + // `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>` (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) if a_tys.len() == b_tys.len() && !a_tys.is_empty() => { - let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); - let b_last_ty = b_tys.last().unwrap(); - - // Substitute just the tail field of B., and require that they're equal. - let unsized_a_ty = - Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); - ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; - - // Similar to ADTs, require that the rest of the fields are equal. - ecx.add_goal(goal.with( - tcx, - ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + result_to_single( + ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys), + BuiltinImplSource::TupleUnsizing, + ) } - _ => Err(NoSolution), + + _ => vec![], } }) } +} +impl<'tcx> EvalCtxt<'_, 'tcx> { + /// Trait upcasting allows for coercions between trait objects: + /// ```ignore (builtin impl example) + /// trait Super {} + /// trait Trait: Super {} + /// // results in builtin impls upcasting to a super trait + /// impl<'a, 'b: 'a> Unsize<dyn Super + 'a> for dyn Trait + 'b {} + /// // and impls removing auto trait bounds. + /// impl<'a, 'b: 'a> Unsize<dyn Trait + 'a> for dyn Trait + Send + 'b {} + /// ``` fn consider_builtin_dyn_upcast_candidates( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> Vec<CanonicalResponse<'tcx>> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return vec![]; - } - - let tcx = ecx.tcx(); - - let a_ty = goal.predicate.self_ty(); - let b_ty = goal.predicate.trait_ref.args.type_at(1); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { - return vec![]; - }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { - return vec![]; - }; + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + a_region: ty::Region<'tcx>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_region: ty::Region<'tcx>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + let tcx = self.tcx(); + let Goal { predicate: (a_ty, b_ty), .. } = goal; // All of a's auto traits need to be in b's auto traits. let auto_traits_compatible = @@ -498,123 +535,220 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return vec![]; } - let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { - ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal( - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), - ); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - }; + // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with + // the supertrait principal and subtyping the types. + let unsize_dyn_to_principal = + |ecx: &mut Self, principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { + ecx.probe_candidate("upcast dyn to principle").enter( + |ecx| -> Result<_, NoSolution> { + // Require that all of the trait predicates from A match B, except for + // the auto traits. We do this by constructing a new A type with B's + // auto traits, and equating these types. + let new_a_data = principal + .into_iter() + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) + .chain(a_data.iter().filter(|a| { + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) + })) + .chain( + b_data + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); + + // We also require that A's lifetime outlives B's lifetime. + ecx.eq(goal.param_env, new_a_ty, b_ty)?; + ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_region, b_region))); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + ) + }; let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { - responses.push(response); - } - } else if let Some(a_principal) = a_data.principal() - && let Some(b_principal) = b_data.principal() - { - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { - if super_trait_ref.def_id() != b_principal.def_id() { - continue; - } - let erased_trait_ref = super_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { - responses.push(response); - } + if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) { + responses.push((resp, BuiltinImplSource::Misc)); } + } else if let Some(a_principal) = a_data.principal() { + self.walk_vtable( + a_principal.with_self_ty(tcx, a_ty), + |ecx, new_a_principal, _, vtable_vptr_slot| { + if let Ok(resp) = unsize_dyn_to_principal( + ecx, + Some(new_a_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + })), + ) { + responses + .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot })); + } + }, + ); } responses } - fn consider_builtin_discriminant_kind_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + /// ```ignore (builtin impl example) + /// trait Trait { + /// fn foo(&self); + /// } + /// // results in the following builtin impl + /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {} + /// ``` + fn consider_builtin_unsize_to_dyn( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_region: ty::Region<'tcx>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + let tcx = self.tcx(); + let Goal { predicate: (a_ty, _b_ty), .. } = goal; + + // Can only unsize to an object-safe trait + if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { return Err(NoSolution); } - // `DiscriminantKind` is automatically implemented for every type. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } + // Check that the type implements all of the predicates of the trait object. + // (i.e. the principal, all of the associated types match, and any auto traits) + self.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty)))); - fn consider_builtin_destruct_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, - ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { + // The type must be `Sized` to be unsized. + if let Some(sized_def_id) = tcx.lang_items().sized_trait() { + self.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty]))); + } else { return Err(NoSolution); } - if !goal.param_env.is_const() { - // `Destruct` is automatically implemented for every type in - // non-const environments. - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - // FIXME(-Ztrait-solver=next): Implement this when we get const working in the new solver - Err(NoSolution) - } + // The type must outlive the lifetime of the `dyn` we're unsizing into. + self.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - fn consider_builtin_transmute_candidate( - ecx: &mut EvalCtxt<'_, 'tcx>, - goal: Goal<'tcx, Self>, + /// We have the following builtin impls for arrays: + /// ```ignore (builtin impl example) + /// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {} + /// ``` + /// While the impl itself could theoretically not be builtin, + /// the actual unsizing behavior is builtin. Its also easier to + /// make all impls of `Unsize` builtin as we're able to use + /// `#[rustc_deny_explicit_impl]` in this case. + fn consider_builtin_array_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_elem_ty: Ty<'tcx>, + b_elem_ty: Ty<'tcx>, ) -> QueryResult<'tcx> { - if goal.predicate.polarity != ty::ImplPolarity::Positive { - return Err(NoSolution); - } + self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - // `rustc_transmute` does not have support for type or const params - if goal.has_non_region_placeholders() { + /// We generate a builtin `Unsize` impls for structs with generic parameters only + /// mentioned by the last field. + /// ```ignore (builtin impl example) + /// struct Foo<T, U: ?Sized> { + /// sized_field: Vec<T>, + /// unsizable: Box<U>, + /// } + /// // results in the following builtin impl + /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<Foo<T, V>> for Foo<T, U> + /// where + /// Box<U>: Unsize<Box<V>>, + /// {} + /// ``` + fn consider_builtin_struct_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + def: ty::AdtDef<'tcx>, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (_a_ty, b_ty), .. } = goal; + + let unsizing_params = tcx.unsizing_params_for_adt(def.did()); + // We must be unsizing some type parameters. This also implies + // that the struct has a tail field. + if unsizing_params.is_empty() { return Err(NoSolution); } - // Erase regions because we compute layouts in `rustc_transmute`, - // which will ICE for region vars. - let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); + let tail_field = def.non_enum_variant().tail(); + let tail_field_ty = tcx.type_of(tail_field.did); + + let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); + let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); + + // Substitute just the unsizing params from B into A. The type after + // this substitution must be equal to B. This is so we don't unsize + // unrelated type parameters. + let new_a_args = tcx.mk_args_from_iter( + a_args + .iter() + .enumerate() + .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), + ); + let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args); + + // Finally, we require that `TailA: Unsize<TailB>` for the tail field + // types. + self.eq(goal.param_env, unsized_a_ty, b_ty)?; + self.add_goal(goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_tail_ty, b_tail_ty], + ), + )); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } - let Some(assume) = - rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) - else { - return Err(NoSolution); - }; + /// We generate the following builtin impl for tuples of all sizes. + /// + /// This impl is still unstable and we emit a feature error when it + /// when it is used by a coercion. + /// ```ignore (builtin impl example) + /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<(T, V)> for (T, U) + /// where + /// U: Unsize<V>, + /// {} + /// ``` + fn consider_builtin_tuple_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_tys: &'tcx ty::List<Ty<'tcx>>, + b_tys: &'tcx ty::List<Ty<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (_a_ty, b_ty), .. } = goal; - let certainty = ecx.is_transmutable( - rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, - args.type_at(2), - assume, - )?; - ecx.evaluate_added_goals_and_make_canonical_response(certainty) + let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); + let &b_last_ty = b_tys.last().unwrap(); + + // Substitute just the tail field of B., and require that they're equal. + let unsized_a_ty = + Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty])); + self.eq(goal.param_env, unsized_a_ty, b_ty)?; + + // Similar to ADTs, require that we can unsize the tail. + self.add_goal(goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_last_ty, b_last_ty], + ), + )); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } -} -impl<'tcx> EvalCtxt<'_, 'tcx> { // Return `Some` if there is an impl (built-in or user provided) that may // hold for the self type of the goal, which for coherence and soundness // purposes must disqualify the built-in auto impl assembled by considering @@ -686,7 +820,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Tuple(_) | ty::Adt(_, _) // FIXME: Handling opaques here is kinda sus. Especially because we - // simplify them to PlaceholderSimplifiedType. + // simplify them to SimplifiedType::Placeholder. | ty::Alias(ty::Opaque, _) => { let mut disqualifying_impl = None; self.tcx().for_each_relevant_impl_treating_projections( @@ -743,4 +877,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) } + + /// Normalize a non-self type when it is structually matched on when solving + /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` + /// for the self type, but for other goals, additional normalization of other + /// arguments may be needed to completely implement the semantics of the trait. + /// + /// This is required when structurally matching on any trait argument that is + /// not the self type. + fn normalize_non_self_ty( + &mut self, + mut ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Result<Option<Ty<'tcx>>, NoSolution> { + if !matches!(ty.kind(), ty::Alias(..)) { + return Ok(Some(ty)); + } + + self.repeat_while_none( + |_| Ok(None), + |ecx| { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Some(Ok(Some(ty))); + }; + + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = Goal::new( + ecx.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + ecx.add_goal(normalizes_to_goal); + if let Err(err) = ecx.try_evaluate_added_goals() { + return Some(Err(err)); + } + + ty = ecx.resolve_vars_if_possible(normalized_ty); + None + }, + ) + } } diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs index c7717879a4a..54de32cf618 100644 --- a/compiler/rustc_trait_selection/src/solve/weak_types.rs +++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs @@ -1,3 +1,8 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. +//! +//! Since a weak alias is not ambiguous, this just computes the `type_of` of +//! the alias and registers the where-clauses of the type alias. use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::ty; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 27cd0f99f34..94d3cd035aa 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -161,7 +161,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let (full_env, full_user_env) = self .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds) .unwrap_or_else(|| { - panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) + panic!("Failed to fully process: {ty:?} {trait_did:?} {orig_env:?}") }); debug!( @@ -178,7 +178,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { ocx.register_bound(ObligationCause::dummy(), full_env, ty, trait_did); let errors = ocx.select_all_or_error(); if !errors.is_empty() { - panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); + panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}"); } let outlives_env = OutlivesEnvironment::new(full_env); @@ -339,7 +339,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { return None; } } - _ => panic!("Unexpected error for '{:?}': {:?}", ty, result), + _ => panic!("Unexpected error for '{ty:?}': {result:?}"), }; let normalized_preds = @@ -347,14 +347,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { new_env = ty::ParamEnv::new( tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())), param_env.reveal(), - param_env.constness(), ); } let final_user_env = ty::ParamEnv::new( tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())), user_env.reveal(), - user_env.constness(), ); debug!( "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \ @@ -749,7 +747,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // subobligations or getting an error) when we started off with // inference variables if p.term().skip_binder().has_infer_types() { - panic!("Unexpected result when selecting {:?} {:?}", ty, obligation) + panic!("Unexpected result when selecting {ty:?} {obligation:?}") } } } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 039e4ec9afd..19d5baa30ec 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -357,7 +357,7 @@ fn impl_intersection_has_negative_obligation( Err(err) => { tcx.sess.delay_span_bug( tcx.def_span(impl1_def_id), - format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), + format!("failed to fully normalize {impl1_def_id:?}: {err:?}"), ); return false; } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c14839fe9be..14020c05251 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -226,7 +226,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) } - _ => panic!("non-FnLike node found: {:?}", node), + _ => panic!("non-FnLike node found: {node:?}"), }) } @@ -273,10 +273,10 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { found_str, ); - err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); + err.span_label(span, format!("expected {kind} that takes {expected_str}")); if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {}", found_str)); + err.span_label(found_span, format!("takes {found_str}")); // Suggest to take and ignore the arguments with expected_args_length `_`s if // found arguments is empty (assume the user just wants to ignore args in this case). @@ -289,7 +289,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { "consider changing the closure to take and ignore the expected argument{}", pluralize!(expected_args.len()) ), - format!("|{}|", underscores), + format!("|{underscores}|"), Applicability::MachineApplicable, ); } @@ -304,7 +304,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { err.span_suggestion_verbose( found_span, "change the closure to take multiple arguments instead of a single tuple", - format!("|{}|", sugg), + format!("|{sugg}|"), Applicability::MachineApplicable, ); } @@ -687,9 +687,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { let trait_predicate = bound_predicate.rebind(trait_predicate); - let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let trait_predicate = self.resolve_vars_if_possible(trait_predicate); - trait_predicate.remap_constness_diag(obligation.param_env); let predicate_is_const = ty::BoundConstness::ConstIfConst == trait_predicate.skip_binder().constness; @@ -704,9 +703,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .get_parent_trait_ref(obligation.cause.code()) .map(|(t, s)| { ( - format!(" in `{}`", t), - format!("within `{}`, ", t), - s.map(|s| (format!("within this `{}`", t), s)), + format!(" in `{t}`"), + format!("within `{t}`, "), + s.map(|s| (format!("within this `{t}`"), s)), ) }) .unwrap_or_default(); @@ -1071,7 +1070,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // which bounds actually failed to hold. self.tcx.sess.struct_span_err( span, - format!("the type `{}` is not well-formed", ty), + format!("the type `{ty}` is not well-formed"), ) } } @@ -1109,7 +1108,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { let mut diag = self.tcx.sess.struct_span_err( span, - format!("the constant `{}` is not of type `{}`", ct, ty), + format!("the constant `{ct}` is not of type `{ty}`"), ); self.note_type_err( &mut diag, @@ -1981,7 +1980,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if all_traits_equal { format!("\n {}", c.self_ty()) } else { - format!("\n {}", c) + format!("\n {c}") } }) .collect(); @@ -2179,10 +2178,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("trait impl{} with same name found", pluralize!(trait_impls.len())), ); let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); - let crate_msg = format!( - "perhaps two different versions of crate `{}` are being used?", - trait_crate - ); + let crate_msg = + format!("perhaps two different versions of crate `{trait_crate}` are being used?"); err.note(crate_msg); suggested = true; } @@ -2310,7 +2307,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.cancel(); return; } - err.note(format!("cannot satisfy `{}`", predicate)); + err.note(format!("cannot satisfy `{predicate}`")); let impl_candidates = self .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap()); if impl_candidates.len() < 10 { @@ -2374,7 +2371,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(local_def_id) = data.trait_ref.def_id.as_local() && let Some(hir::Node::Item(hir::Item { ident: trait_name, kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), .. })) = self.tcx.hir().find_by_def_id(local_def_id) && let Some(method_ref) = trait_item_refs.iter().find(|item_ref| item_ref.ident == *assoc_item_name) { - err.span_label(method_ref.span, format!("`{}::{}` defined here", trait_name, assoc_item_name)); + err.span_label(method_ref.span, format!("`{trait_name}::{assoc_item_name}` defined here")); } err.span_label(span, format!("cannot {verb} associated {noun} of trait")); @@ -2475,7 +2472,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ErrorCode::E0284, true, ); - err.note(format!("cannot satisfy `{}`", predicate)); + err.note(format!("cannot satisfy `{predicate}`")); err } else { // If we can't find a substitution, just print a generic error @@ -2486,7 +2483,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "type annotations needed: cannot satisfy `{}`", predicate, ); - err.span_label(span, format!("cannot satisfy `{}`", predicate)); + err.span_label(span, format!("cannot satisfy `{predicate}`")); err } } @@ -2514,7 +2511,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "type annotations needed: cannot satisfy `{}`", predicate, ); - err.span_label(span, format!("cannot satisfy `{}`", predicate)); + err.span_label(span, format!("cannot satisfy `{predicate}`")); err } } @@ -2529,7 +2526,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "type annotations needed: cannot satisfy `{}`", predicate, ); - err.span_label(span, format!("cannot satisfy `{}`", predicate)); + err.span_label(span, format!("cannot satisfy `{predicate}`")); err } }; @@ -2566,7 +2563,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } } - let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); crate_names.sort(); crate_names.dedup(); post.sort(); @@ -2593,7 +2590,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { predicate ); let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { - format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),) + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),) } else if post.len() == 1 { format!(": `{}`", post[0]) } else { @@ -2602,7 +2599,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { match (spans.len(), crates.len(), crate_names.len()) { (0, 0, 0) => { - err.note(format!("cannot satisfy `{}`", predicate)); + err.note(format!("cannot satisfy `{predicate}`")); } (0, _, 1) => { err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); @@ -2773,7 +2770,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_suggestion_verbose( span, "consider relaxing the implicit `Sized` restriction", - format!("{} ?Sized", separator), + format!("{separator} ?Sized"), Applicability::MachineApplicable, ); } @@ -2864,7 +2861,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }) .unwrap_or_else(|| { - format!("the trait bound `{}` is not satisfied{}", trait_predicate, post_message) + format!("the trait bound `{trait_predicate}` is not satisfied{post_message}") }) } @@ -3108,11 +3105,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span: Span, ) -> UnsatisfiedConst { let mut unsatisfied_const = UnsatisfiedConst(false); - if trait_predicate.is_const_if_const() && obligation.param_env.is_const() { + if trait_predicate.is_const_if_const() { let non_const_predicate = trait_ref.without_const(); let non_const_obligation = Obligation { cause: obligation.cause.clone(), - param_env: obligation.param_env.without_const(), + param_env: obligation.param_env, predicate: non_const_predicate.to_predicate(self.tcx), recursion_depth: obligation.recursion_depth, }; @@ -3152,11 +3149,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_label( closure_span, - format!("this closure implements `{}`, not `{}`", found_kind, kind), + format!("this closure implements `{found_kind}`, not `{kind}`"), ); err.span_label( obligation.cause.span, - format!("the requirement to implement `{}` derives from here", kind), + format!("the requirement to implement `{kind}` derives from here"), ); // Additional context information explaining why the closure only implements @@ -3383,8 +3380,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let const_span = self.tcx.def_span(uv.def); match self.tcx.sess.source_map().span_to_snippet(const_span) { Ok(snippet) => err.help(format!( - "try adding a `where` bound using this expression: `where [(); {}]:`", - snippet + "try adding a `where` bound using this expression: `where [(); {snippet}]:`" )), _ => err.help("consider adding a `where` bound using this expression"), }; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index fbe8d4a6680..0e73bad1918 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -170,7 +170,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(k) = obligation.cause.span.desugaring_kind() { flags.push((sym::from_desugaring, None)); - flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); + flags.push((sym::from_desugaring, Some(format!("{k:?}")))); } if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { @@ -258,9 +258,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let ty::Array(aty, len) = self_ty.kind() { flags.push((sym::_Self, Some("[]".to_string()))); let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx)); - flags.push((sym::_Self, Some(format!("[{}; _]", aty)))); + flags.push((sym::_Self, Some(format!("[{aty}; _]")))); if let Some(n) = len { - flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n)))); + flags.push((sym::_Self, Some(format!("[{aty}; {n}]")))); } if let Some(def) = aty.ty_adt_def() { // We also want to be able to select the array's type's original @@ -329,18 +329,13 @@ pub struct OnUnimplementedNote { } /// Append a message for `~const Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] pub enum AppendConstMessage { + #[default] Default, Custom(Symbol), } -impl Default for AppendConstMessage { - fn default() -> Self { - AppendConstMessage::Default - } -} - impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -584,7 +579,7 @@ impl<'tcx> OnUnimplementedFormatString { "there is no parameter `{}` on {}", s, if trait_def_id == item_def_id { - format!("trait `{}`", trait_name) + format!("trait `{trait_name}`") } else { "impl".to_string() } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 073a2a2b1a0..31f70bbd34d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -5,6 +5,7 @@ use super::{ PredicateObligation, }; +use crate::errors; use crate::infer::InferCtxt; use crate::traits::{NormalizeExt, ObligationCtxt}; @@ -40,7 +41,6 @@ use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::borrow::Cow; use std::iter; -use std::ops::Deref; use super::InferCtxtPrivExt; use crate::infer::InferCtxtExt as _; @@ -479,13 +479,13 @@ fn suggest_restriction<'tcx>( .visit_ty(input); } // The type param `T: Trait` we will suggest to introduce. - let type_param = format!("{}: {}", type_param_name, bound_str); + let type_param = format!("{type_param_name}: {bound_str}"); let mut sugg = vec![ if let Some(span) = hir_generics.span_for_param_suggestion() { - (span, format!(", {}", type_param)) + (span, format!(", {type_param}")) } else { - (hir_generics.span, format!("<{}>", type_param)) + (hir_generics.span, format!("<{type_param}>")) }, // `fn foo(t: impl Trait)` // ^ suggest `where <T as Trait>::A: Bound` @@ -530,7 +530,7 @@ fn suggest_restriction<'tcx>( err.span_suggestion_verbose( sp, - format!("consider further restricting {}", msg), + format!("consider further restricting {msg}"), suggestion, Applicability::MachineApplicable, ); @@ -654,6 +654,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn(_, generics, _) | hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::TraitAlias(generics, _) | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), .. @@ -693,7 +694,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { term ); } else { - constraint.push_str(&format!("<{} = {}>", name, term)); + constraint.push_str(&format!("<{name} = {term}>")); } } @@ -719,6 +720,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | hir::ItemKind::Impl(hir::Impl { generics, .. }) | hir::ItemKind::Fn(_, generics, _) | hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::Const(_, generics, _) | hir::ItemKind::TraitAlias(generics, _) | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), .. @@ -776,18 +778,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { real_trait_pred = parent_trait_pred; } - let real_ty = real_trait_pred.self_ty(); // We `erase_late_bound_regions` here because `make_subregion` does not handle // `ReLateBound`, and we don't particularly care about the regions. - if !self.can_eq( - obligation.param_env, - self.tcx.erase_late_bound_regions(real_ty), - arg_ty, - ) { + let real_ty = self.tcx.erase_late_bound_regions(real_trait_pred.self_ty()); + if !self.can_eq(obligation.param_env, real_ty, arg_ty) { continue; } - if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { + if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { let autoderef = (self.autoderef_steps)(base_ty); if let Some(steps) = autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { @@ -1018,7 +1016,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let name = self.tcx.def_path_str(def_id); err.span_label( self.tcx.def_span(def_id), - format!("consider calling the constructor for `{}`", name), + format!("consider calling the constructor for `{name}`"), ); name } @@ -1397,7 +1395,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Because of this, we modify the error to refer to the original obligation and // return early in the caller. - let msg = format!("the trait bound `{}` is not satisfied", old_pred); + let msg = format!("the trait bound `{old_pred}` is not satisfied"); if has_custom_message { err.note(msg); } else { @@ -1437,7 +1435,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.multipart_suggestion_verbose( sugg_msg, vec![ - (span.shrink_to_lo(), format!("({}", sugg_prefix)), + (span.shrink_to_lo(), format!("({sugg_prefix}")), (span.shrink_to_hi(), ")".to_string()), ], Applicability::MaybeIncorrect, @@ -1470,10 +1468,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let span = if needs_parens { span } else { span.shrink_to_lo() }; let suggestions = if !needs_parens { - vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))] + vec![(span.shrink_to_lo(), sugg_prefix)] } else { vec![ - (span.shrink_to_lo(), format!("{}(", sugg_prefix)), + (span.shrink_to_lo(), format!("{sugg_prefix}(")), (span.shrink_to_hi(), ")".to_string()), ] }; @@ -1704,8 +1702,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { hir.get_if_local(*def_id) { let msg = format!( - "alternatively, consider making `fn {}` asynchronous", - ident + "alternatively, consider making `fn {ident}` asynchronous" ); if vis_span.is_empty() { err.span_suggestion_verbose( @@ -1956,10 +1953,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // don't print out the [type error] here err.delay_as_bug(); } else { - err.span_label( - expr.span, - format!("this returned value is of type `{}`", ty), - ); + err.span_label(expr.span, format!("this returned value is of type `{ty}`")); } } } @@ -2460,8 +2454,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.clear_code(); err.set_primary_message(format!( - "{} cannot be {} between threads safely", - future_or_generator, trait_verb + "{future_or_generator} cannot be {trait_verb} between threads safely" )); let original_span = err.span.primary_span().unwrap(); @@ -2470,7 +2463,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let message = outer_generator .and_then(|generator_did| { Some(match self.tcx.generator_kind(generator_did).unwrap() { - GeneratorKind::Gen => format!("generator is not {}", trait_name), + GeneratorKind::Gen => format!("generator is not {trait_name}"), GeneratorKind::Async(AsyncGeneratorKind::Fn) => self .tcx .parent(generator_did) @@ -2478,73 +2471,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .map(|parent_did| hir.local_def_id_to_hir_id(parent_did)) .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) .map(|name| { - format!("future returned by `{}` is not {}", name, trait_name) + format!("future returned by `{name}` is not {trait_name}") })?, GeneratorKind::Async(AsyncGeneratorKind::Block) => { - format!("future created by async block is not {}", trait_name) + format!("future created by async block is not {trait_name}") } GeneratorKind::Async(AsyncGeneratorKind::Closure) => { - format!("future created by async closure is not {}", trait_name) + format!("future created by async closure is not {trait_name}") } }) }) - .unwrap_or_else(|| format!("{} is not {}", future_or_generator, trait_name)); + .unwrap_or_else(|| format!("{future_or_generator} is not {trait_name}")); span.push_span_label(original_span, message); err.set_span(span); - format!("is not {}", trait_name) + format!("is not {trait_name}") } else { format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path()) }; - let mut explain_yield = - |interior_span: Span, yield_span: Span, scope_span: Option<Span>| { - let mut span = MultiSpan::from_span(yield_span); - let snippet = match source_map.span_to_snippet(interior_span) { - // #70935: If snippet contains newlines, display "the value" instead - // so that we do not emit complex diagnostics. - Ok(snippet) if !snippet.contains('\n') => format!("`{}`", snippet), - _ => "the value".to_string(), - }; - // note: future is not `Send` as this value is used across an await - // --> $DIR/issue-70935-complex-spans.rs:13:9 - // | - // LL | baz(|| async { - // | ______________- - // | | - // | | - // LL | | foo(tx.clone()); - // LL | | }).await; - // | | - ^^^^^^ await occurs here, with value maybe used later - // | |__________| - // | has type `closure` which is not `Send` - // note: value is later dropped here - // LL | | }).await; - // | | ^ - // - span.push_span_label( - yield_span, - format!("{} occurs here, with {} maybe used later", await_or_yield, snippet), - ); - span.push_span_label( - interior_span, - format!("has type `{}` which {}", target_ty, trait_explanation), - ); - if let Some(scope_span) = scope_span { - let scope_span = source_map.end_point(scope_span); + let mut explain_yield = |interior_span: Span, + yield_span: Span, + scope_span: Option<Span>| { + let mut span = MultiSpan::from_span(yield_span); + let snippet = match source_map.span_to_snippet(interior_span) { + // #70935: If snippet contains newlines, display "the value" instead + // so that we do not emit complex diagnostics. + Ok(snippet) if !snippet.contains('\n') => format!("`{snippet}`"), + _ => "the value".to_string(), + }; + // note: future is not `Send` as this value is used across an await + // --> $DIR/issue-70935-complex-spans.rs:13:9 + // | + // LL | baz(|| async { + // | ______________- + // | | + // | | + // LL | | foo(tx.clone()); + // LL | | }).await; + // | | - ^^^^^^ await occurs here, with value maybe used later + // | |__________| + // | has type `closure` which is not `Send` + // note: value is later dropped here + // LL | | }).await; + // | | ^ + // + span.push_span_label( + yield_span, + format!("{await_or_yield} occurs here, with {snippet} maybe used later"), + ); + span.push_span_label( + interior_span, + format!("has type `{target_ty}` which {trait_explanation}"), + ); + if let Some(scope_span) = scope_span { + let scope_span = source_map.end_point(scope_span); - let msg = format!("{} is later dropped here", snippet); - span.push_span_label(scope_span, msg); - } - err.span_note( + let msg = format!("{snippet} is later dropped here"); + span.push_span_label(scope_span, msg); + } + err.span_note( span, format!( - "{} {} as this value is used across {}", - future_or_generator, trait_explanation, an_await_or_yield + "{future_or_generator} {trait_explanation} as this value is used across {an_await_or_yield}" ), ); - }; + }; match interior_or_upvar_span { GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info { @@ -2554,15 +2547,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span.push_span_label( await_span, format!( - "await occurs here on type `{}`, which {}", - target_ty, trait_explanation + "await occurs here on type `{target_ty}`, which {trait_explanation}" ), ); err.span_note( span, format!( - "future {not_trait} as it awaits another future which {not_trait}", - not_trait = trait_explanation + "future {trait_explanation} as it awaits another future which {trait_explanation}" ), ); } else { @@ -2645,18 +2636,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let ref_kind = if is_mut { "&mut" } else { "&" }; ( format!( - "has type `{}` which {}, because `{}` is not `{}`", - target_ty, trait_explanation, ref_ty, ref_ty_trait + "has type `{target_ty}` which {trait_explanation}, because `{ref_ty}` is not `{ref_ty_trait}`" ), format!( - "captured value {} because `{}` references cannot be sent unless their referent is `{}`", - trait_explanation, ref_kind, ref_ty_trait + "captured value {trait_explanation} because `{ref_kind}` references cannot be sent unless their referent is `{ref_ty_trait}`" ), ) } None => ( - format!("has type `{}` which {}", target_ty, trait_explanation), - format!("captured value {}", trait_explanation), + format!("has type `{target_ty}` which {trait_explanation}"), + format!("captured value {trait_explanation}"), ), }; @@ -2745,8 +2734,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::ObjectTypeBound(object_ty, region) => { err.note(format!( - "required so that the lifetime bound of `{}` for `{}` is satisfied", - region, object_ty, + "required so that the lifetime bound of `{region}` for `{object_ty}` is satisfied", )); } ObligationCauseCode::ItemObligation(_) @@ -3066,7 +3054,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut msg = "required because it captures the following types: ".to_owned(); for ty in bound_tys.skip_binder() { - with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap()); + with_forced_trimmed_paths!(write!(msg, "`{ty}`, ").unwrap()); } err.note(msg.trim_end_matches(", ").to_string()) } @@ -3080,7 +3068,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "required because it captures the following types: ".to_owned(); for bty in tcx.generator_hidden_types(*def_id) { let ty = bty.instantiate(tcx, args); - write!(msg, "`{}`, ", ty).unwrap(); + write!(msg, "`{ty}`, ").unwrap(); } err.note(msg.trim_end_matches(", ").to_string()) } @@ -3139,7 +3127,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ObligationCauseCode::ImplDerivedObligation(ref data) => { let mut parent_trait_pred = self.resolve_vars_if_possible(data.derived.parent_trait_pred); - parent_trait_pred.remap_constness_diag(param_env); let parent_def_id = parent_trait_pred.def_id(); let (self_ty, file) = self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty()); @@ -3516,7 +3503,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred.skip_binder().self_ty(), diagnostic_name, ), - format!("#[derive({})]\n", diagnostic_name), + format!("#[derive({diagnostic_name})]\n"), Applicability::MaybeIncorrect, ); } @@ -3579,7 +3566,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // to an associated type (as seen from `trait_pred`) in the predicate. Like in // trait_pred `S: Sum<<Self as Iterator>::Item>` and predicate `i32: Sum<&()>` let mut type_diffs = vec![]; - if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref() + if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code && let Some(node_args) = typeck_results.node_args_opt(call_hir_id) && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args) && let Some(where_pred) = where_clauses.predicates.get(*idx) @@ -4002,7 +3989,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .map(|trait_ref| trait_ref.trait_ref.self_ty()) .find(|t| is_slice(*t)) { - let msg = format!("convert the array to a `{}` slice instead", slice_ty); + let msg = format!("convert the array to a `{slice_ty}` slice instead"); if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let mut suggestions = vec![]; @@ -4031,6 +4018,10 @@ fn hint_missing_borrow<'tcx>( found_node: Node<'_>, err: &mut Diagnostic, ) { + if matches!(found_node, Node::TraitItem(..)) { + return; + } + let found_args = match found.kind() { ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(), kind => { @@ -4102,19 +4093,11 @@ fn hint_missing_borrow<'tcx>( } if !to_borrow.is_empty() { - err.multipart_suggestion_verbose( - "consider borrowing the argument", - to_borrow, - Applicability::MaybeIncorrect, - ); + err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow }); } if !remove_borrow.is_empty() { - err.multipart_suggestion_verbose( - "do not borrow the argument", - remove_borrow, - Applicability::MaybeIncorrect, - ); + err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow }); } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 1c145999184..593f669e9bb 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -328,11 +328,7 @@ pub fn normalize_param_env_or_error<'tcx>( debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); - let elaborated_env = ty::ParamEnv::new( - tcx.mk_clauses(&predicates), - unnormalized_env.reveal(), - unnormalized_env.constness(), - ); + let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()); // HACK: we are trying to normalize the param-env inside *itself*. The problem is that // normalization expects its param-env to be already normalized, which means we have @@ -376,11 +372,8 @@ pub fn normalize_param_env_or_error<'tcx>( // here. I believe they should not matter, because we are ignoring TypeOutlives param-env // predicates here anyway. Keeping them here anyway because it seems safer. let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned(); - let outlives_env = ty::ParamEnv::new( - tcx.mk_clauses_from_iter(outlives_env), - unnormalized_env.reveal(), - unnormalized_env.constness(), - ); + let outlives_env = + ty::ParamEnv::new(tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal()); let Ok(outlives_predicates) = do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates) else { @@ -393,11 +386,7 @@ pub fn normalize_param_env_or_error<'tcx>( let mut predicates = non_outlives_predicates; predicates.extend(outlives_predicates); debug!("normalize_param_env_or_error: final predicates={:?}", predicates); - ty::ParamEnv::new( - tcx.mk_clauses(&predicates), - unnormalized_env.reveal(), - unnormalized_env.constness(), - ) + ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()) } /// Normalize a type and process all resulting obligations, returning any errors. @@ -474,11 +463,14 @@ fn subst_and_check_impossible_predicates<'tcx>( result } -/// Checks whether a trait's method is impossible to call on a given impl. +/// Checks whether a trait's associated item is impossible to reference on a given impl. /// /// This only considers predicates that reference the impl's generics, and not /// those that reference the method's generics. -fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefId, DefId)) -> bool { +fn is_impossible_associated_item( + tcx: TyCtxt<'_>, + (impl_def_id, trait_item_def_id): (DefId, DefId), +) -> bool { struct ReferencesOnlyParentGenerics<'tcx> { tcx: TyCtxt<'tcx>, generics: &'tcx ty::Generics, @@ -556,7 +548,7 @@ pub fn provide(providers: &mut Providers) { specializes: specialize::specializes, subst_and_check_impossible_predicates, check_tys_might_be_eq: misc::check_tys_might_be_eq, - is_impossible_method, + is_impossible_associated_item, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 143e8412967..76752edc4ae 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -517,8 +517,7 @@ fn virtual_call_violation_for_method<'tcx>( tcx.sess.delay_span_bug( tcx.def_span(method.def_id), format!( - "receiver when `Self = ()` should have a Scalar ABI; found {:?}", - abi + "receiver when `Self = ()` should have a Scalar ABI; found {abi:?}" ), ); } @@ -536,8 +535,7 @@ fn virtual_call_violation_for_method<'tcx>( tcx.sess.delay_span_bug( tcx.def_span(method.def_id), format!( - "receiver when `Self = {}` should have a ScalarPair ABI; found {:?}", - trait_object_ty, abi + "receiver when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" ), ); } @@ -761,11 +759,7 @@ fn receiver_is_dispatchable<'tcx>( let caller_bounds = param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]); - ty::ParamEnv::new( - tcx.mk_clauses_from_iter(caller_bounds), - param_env.reveal(), - param_env.constness(), - ) + ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal()) }; // Receiver: DispatchFromDyn<Receiver[Self => U]> diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index a39fc1f1771..98f45826727 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -4,7 +4,6 @@ use super::check_args_compatible; use super::specialization_graph; use super::translate_args; use super::util; -use super::ImplSourceUserDefinedData; use super::MismatchedProjectionTypes; use super::Obligation; use super::ObligationCause; @@ -13,6 +12,9 @@ use super::Selection; use super::SelectionContext; use super::SelectionError; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::traits::ImplSource; +use rustc_middle::traits::ImplSourceUserDefinedData; use crate::errors::InherentProjectionNormalizationOverflow; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -481,8 +483,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { assert!( !value.has_escaping_bound_vars(), - "Normalizing {:?} without wrapping in a `Binder`", - value + "Normalizing {value:?} without wrapping in a `Binder`" ); if !needs_normalization(&value, self.param_env.reveal()) { @@ -1717,7 +1718,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( }; let eligible = match &impl_source { - super::ImplSource::UserDefined(impl_data) => { + ImplSource::UserDefined(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in // codegen (i.e., projection mode is not "any"), and the @@ -1767,7 +1768,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } - super::ImplSource::Builtin(..) => { + ImplSource::Builtin(BuiltinImplSource::Misc, _) => { // While a builtin impl may be known to exist, the associated type may not yet // be known. Any type with multiple potential associated types is therefore // not eligible. @@ -1891,7 +1892,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( bug!("unexpected builtin trait with associated type: {trait_ref:?}") } } - super::ImplSource::Param(..) => { + ImplSource::Param(..) => { // This case tell us nothing about the value of an // associated type. Consider: // @@ -1919,17 +1920,18 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // in `assemble_candidates_from_param_env`. false } - super::ImplSource::Object(_) => { + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => { // Handled by the `Object` projection candidate. See // `assemble_candidates_from_object_ty` for an explanation of // why we special case object types. false } - | super::ImplSource::TraitUpcasting(_) => { + ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, - format!("Cannot project an associated type from `{:?}`", impl_source), + format!("Cannot project an associated type from `{impl_source:?}`"), ); return Err(()); } @@ -1985,8 +1987,8 @@ fn confirm_select_candidate<'cx, 'tcx>( impl_source: Selection<'tcx>, ) -> Progress<'tcx> { match impl_source { - super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), - super::ImplSource::Builtin(data) => { + ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), + ImplSource::Builtin(BuiltinImplSource::Misc, data) => { let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); let lang_items = selcx.tcx().lang_items(); if lang_items.gen_trait() == Some(trait_def_id) { @@ -2003,9 +2005,10 @@ fn confirm_select_candidate<'cx, 'tcx>( confirm_builtin_candidate(selcx, obligation, data) } } - super::ImplSource::Object(_) - | super::ImplSource::Param(..) - | super::ImplSource::TraitUpcasting(_) => { + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) + | ImplSource::Param(..) + | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, @@ -2299,8 +2302,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( } Err(e) => { let msg = format!( - "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", - obligation, poly_cache_entry, e, + "Failed to unify obligation `{obligation:?}` with poly_projection `{poly_cache_entry:?}`: {e:?}", ); debug!("confirm_param_env_candidate: {}", msg); let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg); diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index a50644bb709..65f32b1c48a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,5 +1,4 @@ use rustc_infer::traits::{TraitEngine, TraitEngineExt}; -use rustc_middle::ty; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; @@ -66,17 +65,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { ) -> Result<EvaluationResult, OverflowError> { let mut _orig_values = OriginalQueryValues::default(); - let param_env = match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { - // we ignore the value set to it. - let mut _constness = pred.constness; - obligation - .param_env - .with_constness(_constness.and(obligation.param_env.constness())) - } - // constness has no effect on the given predicate. - _ => obligation.param_env.without_const(), - }; + let param_env = obligation.param_env; if self.next_trait_solver() { self.probe(|snapshot| { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index d45cf94f731..87beaddc6c2 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -299,7 +299,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> if !tcx.sess.opts.actually_rustdoc { tcx.sess.delay_span_bug( DUMMY_SP, - format!("unexpected ambiguity: {:?} {:?}", c_data, result), + format!("unexpected ambiguity: {c_data:?} {result:?}"), ); } return Err(NoSolution); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 420d6326553..302b6016e5e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -82,7 +82,6 @@ fn relate_mir_and_user_args<'tcx>( def_id: DefId, user_args: UserArgs<'tcx>, ) -> Result<(), NoSolution> { - let param_env = param_env.without_const(); let UserArgs { user_self_ty, args } = user_args; let tcx = ocx.infcx.tcx; let cause = ObligationCause::dummy_with_span(span); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 5420caee329..c99e018e9ae 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -77,8 +77,7 @@ where let pre_obligations = infcx.take_registered_region_obligations(); assert!( pre_obligations.is_empty(), - "scrape_region_constraints: incoming region obligations = {:#?}", - pre_obligations, + "scrape_region_constraints: incoming region obligations = {pre_obligations:#?}", ); let value = infcx.commit_if_ok(|_| { @@ -92,7 +91,7 @@ where } else { Err(infcx.tcx.sess.delay_span_bug( DUMMY_SP, - format!("errors selecting obligation during MIR typeck: {:?}", errors), + format!("errors selecting obligation during MIR typeck: {errors:?}"), )) } })?; diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index aa195d70a9f..b9f31be25b1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -124,11 +124,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidates_from_projected_tys(obligation, &mut candidates); self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; - // Auto implementations have lower priority, so we only - // consider triggering a default if there is no other impl that can apply. - if candidates.vec.is_empty() { - self.assemble_candidates_from_auto_impls(obligation, &mut candidates); - } + self.assemble_candidates_from_auto_impls(obligation, &mut candidates); } debug!("candidate list size: {}", candidates.vec.len()); Ok(candidates) @@ -513,7 +509,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // for an example of a test case that exercises // this path. } - ty::Infer(ty::TyVar(_)) => { + ty::Infer(ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_)) => { // The auto impl might apply; we don't know. candidates.ambiguous = true; } @@ -533,7 +529,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - _ => candidates.vec.push(AutoImplCandidate), + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!( + "asked to assemble auto trait candidates of unexpected type: {:?}", + self_ty + ); + } + + ty::Alias(_, _) + if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(..))) => + { + // We do not generate an auto impl candidate for `impl Trait`s which already + // reference our auto trait. + // + // For example during candidate assembly for `impl Send: Send`, we don't have + // to look at the constituent types for this opaque types to figure out that this + // trivially holds. + // + // Note that this is only sound as projection candidates of opaque types + // are always applicable for auto traits. + } + ty::Alias(_, _) => candidates.vec.push(AutoImplCandidate), + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::Adt(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Closure(_, _) + | ty::Generator(..) + | ty::Never + | ty::Tuple(_) + | ty::GeneratorWitness(_) + | ty::GeneratorWitnessMIR(..) => { + // Only consider auto impls if there are no manual impls for the root of `self_ty`. + // + // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl + // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls + // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined. + // + // Generally, we have to guarantee that for all `SimplifiedType`s the only crate + // which may define impls for that type is either the crate defining the type + // or the trait. This should be guaranteed by the orphan check. + let mut has_impl = false; + self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true); + if !has_impl { + candidates.vec.push(AutoImplCandidate) + } + } + ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now. } } } @@ -839,7 +891,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) { // If the predicate is `~const Destruct` in a non-const environment, we don't actually need // to check anything. We'll short-circuit checking any obligations in confirmation, too. - if !obligation.is_const() { + // FIXME(effects) + if true { candidates.vec.push(ConstDestructCandidate(None)); return; } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2cb2895b476..7140fedb74a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,10 +11,10 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; -use rustc_middle::traits::SelectionOutputTypeParameterMismatch; +use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; use rustc_middle::ty::{ - self, Binder, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, - TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, + self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, + TraitPredicate, Ty, TyCtxt, TypeVisitableExt, }; use rustc_span::def_id::DefId; @@ -26,9 +26,9 @@ use crate::traits::vtable::{ }; use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, - ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, - Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation, - PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented, + ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection, + SelectionError, TraitNotObjectSafe, Unimplemented, }; use super::BuiltinImplConditions; @@ -48,18 +48,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut impl_src = match candidate { BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } TransmutabilityCandidate => { let data = self.confirm_transmutability_candidate(obligation)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - ImplSource::Param(obligations, param.skip_binder().constness) + ImplSource::Param(param.skip_binder().constness, obligations) } ImplCandidate(impl_def_id) => { @@ -68,64 +68,57 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { AutoImplCandidate => { let data = self.confirm_auto_impl_candidate(obligation)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } ProjectionCandidate(idx, constness) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; - ImplSource::Param(obligations, constness) + ImplSource::Param(constness, obligations) } - ObjectCandidate(idx) => { - let data = self.confirm_object_candidate(obligation, idx)?; - ImplSource::Object(data) - } + ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?, ClosureCandidate { .. } => { let vtable_closure = self.confirm_closure_candidate(obligation)?; - ImplSource::Builtin(vtable_closure) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure) } GeneratorCandidate => { let vtable_generator = self.confirm_generator_candidate(obligation)?; - ImplSource::Builtin(vtable_generator) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_generator) } FutureCandidate => { let vtable_future = self.confirm_future_candidate(obligation)?; - ImplSource::Builtin(vtable_future) + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future) } FnPointerCandidate { is_const } => { let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } TraitAliasCandidate => { let data = self.confirm_trait_alias_candidate(obligation); - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really // no additional obligations to prove and no types in particular to unify, etc. - ImplSource::Builtin(Vec::new()) + ImplSource::Builtin(BuiltinImplSource::Misc, Vec::new()) } - BuiltinUnsizeCandidate => { - let data = self.confirm_builtin_unsize_candidate(obligation)?; - ImplSource::Builtin(data) - } + BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?, TraitUpcastingUnsizeCandidate(idx) => { - let data = self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?; - ImplSource::TraitUpcasting(data) + self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? } ConstDestructCandidate(def_id) => { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::Builtin(data) + ImplSource::Builtin(BuiltinImplSource::Misc, data) } }; @@ -484,7 +477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, index: usize, - ) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); debug!(?obligation, ?index, "confirm_object_candidate"); @@ -648,13 +641,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), ); - Ok(ImplSourceObjectData { vtable_base, nested }) + Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested)) } fn confirm_fn_pointer_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - is_const: bool, + // FIXME(effects) + _is_const: bool, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); @@ -681,16 +675,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; let cause = obligation.derived_cause(BuiltinDerivedObligation); - if obligation.is_const() && !is_const { - // function is a trait method - if let ty::FnDef(def_id, args) = self_ty.kind() && let Some(trait_id) = tcx.trait_of_item(*def_id) { - let trait_ref = TraitRef::from_method(tcx, trait_id, *args); - let poly_trait_pred = Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst); - let obligation = Obligation::new(tcx, cause.clone(), obligation.param_env, poly_trait_pred); - nested.push(obligation); - } - } - // Confirm the `type Output: Sized;` bound that is present on `FnOnce` let output_ty = self.infcx.instantiate_binder_with_placeholders(sig.output()); let output_ty = normalize_with_depth_to( @@ -897,7 +881,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &PolyTraitObligation<'tcx>, idx: usize, - ) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -994,13 +978,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let vtable_vptr_slot = prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); - Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) } fn confirm_builtin_unsize_candidate( &mut self, obligation: &PolyTraitObligation<'tcx>, - ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -1008,10 +992,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); let target = self.infcx.shallow_resolve(target); - debug!(?source, ?target, "confirm_builtin_unsize_candidate"); let mut nested = vec![]; + let src; match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b)) @@ -1055,6 +1039,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.predicate.rebind(outlives), )); + + src = BuiltinImplSource::Misc; } // `T` -> `Trait` @@ -1101,6 +1087,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested.push(predicate_to_obligation( ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), )); + + src = BuiltinImplSource::Misc; } // `[T; n]` -> `[T]` @@ -1111,6 +1099,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .eq(DefineOpaqueTypes::No, b, a) .map_err(|_| Unimplemented)?; nested.extend(obligations); + + src = BuiltinImplSource::Misc; } // `Struct<T>` -> `Struct<U>` @@ -1167,6 +1157,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ), ); nested.push(tail_unsize_obligation); + + src = BuiltinImplSource::Misc; } // `(.., T)` -> `(.., U)` @@ -1194,12 +1186,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), ); nested.push(last_unsize_obligation); + + src = BuiltinImplSource::TupleUnsizing; } _ => bug!("source: {source}, target: {target}"), }; - Ok(nested) + Ok(ImplSource::Builtin(src, nested)) } fn confirm_const_destruct_candidate( @@ -1208,7 +1202,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_def_id: Option<DefId>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { // `~const Destruct` in a non-const environment is always trivially true, since our type is `Drop` - if !obligation.is_const() { + // FIXME(effects) + if true { return Ok(vec![]); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e086489b1bc..c29696bc817 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -74,22 +74,21 @@ impl IntercrateAmbiguityCause { match self { IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } => { let self_desc = if let Some(ty) = self_desc { - format!(" for type `{}`", ty) + format!(" for type `{ty}`") } else { String::new() }; - format!("downstream crates may implement trait `{}`{}", trait_desc, self_desc) + format!("downstream crates may implement trait `{trait_desc}`{self_desc}") } IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } => { let self_desc = if let Some(ty) = self_desc { - format!(" for type `{}`", ty) + format!(" for type `{ty}`") } else { String::new() }; format!( - "upstream crates may add a new impl of trait `{}`{} \ - in future versions", - trait_desc, self_desc + "upstream crates may add a new impl of trait `{trait_desc}`{self_desc} \ + in future versions" ) } IntercrateAmbiguityCause::ReservationImpl { message } => message.clone(), @@ -1000,13 +999,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } let stack = self.push_stack(previous_stack, &obligation); - let mut fresh_trait_pred = stack.fresh_trait_pred; - let mut param_env = obligation.param_env; - - fresh_trait_pred = fresh_trait_pred.map_bound(|mut pred| { - pred.remap_constness(&mut param_env); - pred - }); + let fresh_trait_pred = stack.fresh_trait_pred; + let param_env = obligation.param_env; debug!(?fresh_trait_pred); @@ -1386,8 +1380,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (result, dep_node) } - /// filter_impls filters constant trait obligations and candidates that have a positive impl - /// for a negative goal and a negative impl for a positive goal + /// filter_impls filters candidates that have a positive impl for a negative + /// goal and a negative impl for a positive goal #[instrument(level = "debug", skip(self, candidates))] fn filter_impls( &mut self, @@ -1399,42 +1393,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut result = Vec::with_capacity(candidates.len()); for candidate in candidates { - // Respect const trait obligations - if obligation.is_const() { - match candidate { - // const impl - ImplCandidate(def_id) if tcx.constness(def_id) == hir::Constness::Const => {} - // const param - ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {} - // const projection - ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) - // auto trait impl - | AutoImplCandidate - // generator / future, this will raise error in other places - // or ignore error with const_async_blocks feature - | GeneratorCandidate - | FutureCandidate - // FnDef where the function is const - | FnPointerCandidate { is_const: true } - | ConstDestructCandidate(_) - | ClosureCandidate { is_const: true } => {} - - FnPointerCandidate { is_const: false } => { - if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() { - // Trait methods are not seen as const unless the trait is implemented as const. - // We do not filter that out in here, but nested obligations will be needed to confirm this. - } else { - continue - } - } - - _ => { - // reject all other types of candidates - continue; - } - } - } - if let ImplCandidate(def_id) = candidate { if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id) || obligation.polarity() == tcx.impl_polarity(def_id) @@ -1487,7 +1445,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> { debug!("is_knowable(intercrate={:?})", self.is_intercrate()); - if !self.is_intercrate() || stack.obligation.polarity() == ty::ImplPolarity::Negative { + if !self.is_intercrate() { return Ok(()); } @@ -1528,7 +1486,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn check_candidate_cache( &mut self, - mut param_env: ty::ParamEnv<'tcx>, + param_env: ty::ParamEnv<'tcx>, cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> { // Neither the global nor local cache is aware of intercrate @@ -1539,8 +1497,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return None; } let tcx = self.tcx(); - let mut pred = cache_fresh_trait_pred.skip_binder(); - pred.remap_constness(&mut param_env); + let pred = cache_fresh_trait_pred.skip_binder(); if self.can_use_global_caches(param_env) { if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) { @@ -1586,15 +1543,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(skip(self, param_env, cache_fresh_trait_pred, dep_node), level = "debug")] fn insert_candidate_cache( &mut self, - mut param_env: ty::ParamEnv<'tcx>, + param_env: ty::ParamEnv<'tcx>, cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, dep_node: DepNodeIndex, candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, ) { let tcx = self.tcx(); - let mut pred = cache_fresh_trait_pred.skip_binder(); - - pred.remap_constness(&mut param_env); + let pred = cache_fresh_trait_pred.skip_binder(); if !self.can_cache_candidate(&candidate) { debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable"); @@ -1827,6 +1782,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { /// candidates and prefer where-clause candidates. /// /// See the comment for "SelectionCandidate" for more details. + #[instrument(level = "debug", skip(self))] fn candidate_should_be_dropped_in_favor_of( &mut self, victim: &EvaluatedCandidate<'tcx>, @@ -1850,13 +1806,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. match (&other.candidate, &victim.candidate) { - (_, AutoImplCandidate) | (AutoImplCandidate, _) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // FIXME(@jswrenn): this should probably be more sophisticated (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, @@ -1898,6 +1847,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ( ParamCandidate(ref other_cand), ImplCandidate(..) + | AutoImplCandidate | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate @@ -1925,6 +1875,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } ( ImplCandidate(_) + | AutoImplCandidate | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate @@ -1958,6 +1909,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ( ObjectCandidate(_) | ProjectionCandidate(..), ImplCandidate(..) + | AutoImplCandidate | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate @@ -1971,6 +1923,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ( ImplCandidate(..) + | AutoImplCandidate | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate @@ -2061,6 +2014,19 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } + (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => { + DropVictim::No + } + + (AutoImplCandidate, _) | (_, AutoImplCandidate) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other global candidates: {:?} {:?}", + other, + victim + ); + } + // Everything else is ambiguous ( ImplCandidate(_) @@ -2443,8 +2409,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let guar = self.infcx.tcx.sess.delay_span_bug( obligation.cause.span, format!( - "Impl {:?} was matchable against {:?} but now is not", - impl_def_id, obligation + "Impl {impl_def_id:?} was matchable against {obligation:?} but now is not" ), ); let value = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index b61b209654c..61e631c438d 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -388,16 +388,16 @@ fn report_conflicting_impls<'tcx>( impl_span, format!( "conflicting implementation{}", - overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{}`", ty)) + overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{ty}`")) ), ); } Err(cname) => { let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { Some(s) => { - format!("conflicting implementation in crate `{}`:\n- {}", cname, s) + format!("conflicting implementation in crate `{cname}`:\n- {s}") } - None => format!("conflicting implementation in crate `{}`", cname), + None => format!("conflicting implementation in crate `{cname}`"), }; err.note(msg); } @@ -514,8 +514,7 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti pretty_predicates.push(p.to_string()); } - pretty_predicates - .extend(types_without_default_bounds.iter().map(|ty| format!("{}: ?Sized", ty))); + pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized"))); if !pretty_predicates.is_empty() { write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 7b34908c4be..4c27fe8b29b 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -50,7 +50,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { diag.span_label(self.top().1, top_label); if self.path.len() > 1 { for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { - diag.span_label(*sp, format!("referenced here ({})", use_desc)); + diag.span_label(*sp, format!("referenced here ({use_desc})")); } } if self.top().1 != self.bottom().1 { @@ -58,7 +58,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { // redundant labels. diag.span_label( self.bottom().1, - format!("trait alias used in trait object type ({})", use_desc), + format!("trait alias used in trait object type ({use_desc})"), ); } } @@ -241,9 +241,9 @@ pub fn upcast_choices<'tcx>( /// Given an upcast trait object described by `object`, returns the /// index of the method `method_def_id` (which should be part of /// `object.upcast_trait_ref`) within the vtable for `object`. -pub fn get_vtable_index_of_object_method<'tcx, N>( +pub fn get_vtable_index_of_object_method<'tcx>( tcx: TyCtxt<'tcx>, - object: &super::ImplSourceObjectData<N>, + vtable_base: usize, method_def_id: DefId, ) -> Option<usize> { // Count number of methods preceding the one we are selecting and @@ -252,7 +252,7 @@ pub fn get_vtable_index_of_object_method<'tcx, N>( .iter() .copied() .position(|def_id| def_id == method_def_id) - .map(|index| object.vtable_base + index) + .map(|index| vtable_base + index) } pub fn closure_trait_ref_and_return_type<'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 78816fed017..3964dda1fdb 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -5,6 +5,7 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::traits::util::PredicateSet; use rustc_infer::traits::ImplSource; use rustc_middle::query::Providers; +use rustc_middle::traits::BuiltinImplSource; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::GenericArgs; use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; @@ -24,8 +25,18 @@ pub enum VtblSegment<'tcx> { pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, - mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, + segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, ) -> Option<T> { + prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() +} + +/// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`, +/// such that we can use `?` in the body. +fn prepare_vtable_segments_inner<'tcx, T>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, +) -> ControlFlow<T> { // The following constraints holds for the final arrangement. // 1. The whole virtual table of the first direct super trait is included as the // the prefix. If this trait doesn't have any super traits, then this step @@ -71,20 +82,18 @@ pub fn prepare_vtable_segments<'tcx, T>( // N, N-vptr, O // emit dsa segment first. - if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) { - return Some(v); - } + segment_visitor(VtblSegment::MetadataDSA)?; let mut emit_vptr_on_new_entry = false; let mut visited = PredicateSet::new(tcx); let predicate = trait_ref.without_const().to_predicate(tcx); let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = - smallvec![(trait_ref, emit_vptr_on_new_entry, None)]; + smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; visited.insert(predicate); // the main traversal loop: // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes - // that each node is emitted after all its descendents have been emitted. + // such that each node is emitted after all its descendants have been emitted. // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set. // this is done on the fly. // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it @@ -105,98 +114,107 @@ pub fn prepare_vtable_segments<'tcx, T>( // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node. // Loop run #1: Stack after exiting out is []. Now the function exits. - loop { + 'outer: loop { // dive deeper into the stack, recording the path 'diving_in: loop { - if let Some((inner_most_trait_ref, _, _)) = stack.last() { - let inner_most_trait_ref = *inner_most_trait_ref; - let mut direct_super_traits_iter = tcx - .super_predicates_of(inner_most_trait_ref.def_id()) - .predicates - .into_iter() - .filter_map(move |(pred, _)| { - pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() - }); + let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); + + let mut direct_super_traits_iter = tcx + .super_predicates_of(inner_most_trait_ref.def_id()) + .predicates + .into_iter() + .filter_map(move |(pred, _)| { + pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() + }); - 'diving_in_skip_visited_traits: loop { - if let Some(next_super_trait) = direct_super_traits_iter.next() { - if visited.insert(next_super_trait.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); - stack.push(( - next_super_trait, - emit_vptr_on_new_entry, - Some(direct_super_traits_iter), - )); - break 'diving_in_skip_visited_traits; - } else { - continue 'diving_in_skip_visited_traits; - } - } else { - break 'diving_in; - } + // Find an unvisited supertrait + match direct_super_traits_iter + .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx))) + { + // Push it to the stack for the next iteration of 'diving_in to pick up + Some(unvisited_super_trait) => { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); + stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + maybe_iter(Some(direct_super_traits_iter)), + )) } + + // There are no more unvisited direct super traits, dive-in finished + None => break 'diving_in, } } - // Other than the left-most path, vptr should be emitted for each trait. - emit_vptr_on_new_entry = true; - // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. - 'exiting_out: loop { - if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() { - if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries { - trait_ref: *inner_most_trait_ref, - emit_vptr: *emit_vptr, - }) { - return Some(v); - } + while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { + segment_visitor(VtblSegment::TraitOwnEntries { + trait_ref: inner_most_trait_ref, + emit_vptr, + })?; + + // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, + // we'll need to emit vptrs from now on. + if !emit_vptr_on_new_entry + && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) + { + emit_vptr_on_new_entry = true; + } - 'exiting_out_skip_visited_traits: loop { - if let Some(siblings) = siblings_opt { - if let Some(next_inner_most_trait_ref) = siblings.next() { - if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_inner_most_trait_ref = - next_inner_most_trait_ref.map_bound(|t| t.trait_ref); - *inner_most_trait_ref = next_inner_most_trait_ref; - *emit_vptr = emit_vptr_on_new_entry; - break 'exiting_out; - } else { - continue 'exiting_out_skip_visited_traits; - } - } - } - stack.pop(); - continue 'exiting_out; - } + if let Some(next_inner_most_trait_ref) = + siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) + { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_inner_most_trait_ref = + next_inner_most_trait_ref.map_bound(|t| t.trait_ref); + + stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); + + // just pushed a new trait onto the stack, so we need to go through its super traits + continue 'outer; } - // all done - return None; } + + // the stack is empty, all done + return ControlFlow::Continue(()); } } +/// Turns option of iterator into an iterator (this is just flatten) +fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> { + // Flatten is bad perf-vise, we could probably implement a special case here that is better + i.into_iter().flatten() +} + fn dump_vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, trait_ref: ty::PolyTraitRef<'tcx>, entries: &[VtblEntry<'tcx>], ) { - tcx.sess.emit_err(DumpVTableEntries { - span: sp, - trait_ref, - entries: format!("{:#?}", entries), - }); + tcx.sess.emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") }); +} + +fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some() } fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] { + tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id)) +} + +fn own_existential_vtable_entries_iter( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> impl Iterator<Item = DefId> + '_ { let trait_methods = tcx .associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Fn); + // Now list each method's DefId (for within its trait). let own_entries = trait_methods.filter_map(move |&trait_method| { debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); @@ -211,7 +229,7 @@ fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[Def Some(def_id) }); - tcx.arena.alloc_from_iter(own_entries.into_iter()) + own_entries } /// Given a trait `trait_ref`, iterates the vtable entries @@ -363,8 +381,8 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { - Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => { - implsrc_traitcasting.vtable_vptr_slot + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => { + *vtable_vptr_slot } otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 8bd26818256..e8ddb0a4396 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -195,7 +195,7 @@ pub(crate) mod rustc { impl<'tcx> From<&LayoutError<'tcx>> for Err { fn from(err: &LayoutError<'tcx>) -> Self { match err { - LayoutError::Unknown(..) => Self::UnknownLayout, + LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout, err => unimplemented!("{:?}", err), } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 34ad6bd8c69..05ad4a4a12a 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -78,6 +78,7 @@ mod rustc { use rustc_middle::ty::ParamEnv; use rustc_middle::ty::Ty; use rustc_middle::ty::TyCtxt; + use rustc_middle::ty::ValTree; /// The source and destination types of a transmutation. #[derive(TypeVisitable, Debug, Clone, Copy)] @@ -148,7 +149,17 @@ mod rustc { ); let variant = adt_def.non_enum_variant(); - let fields = c.to_valtree().unwrap_branch(); + let fields = match c.try_to_valtree() { + Some(ValTree::Branch(branch)) => branch, + _ => { + return Some(Self { + alignment: true, + lifetimes: true, + safety: true, + validity: true, + }); + } + }; let get_field = |name| { let (field_idx, _) = variant diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 4a544232174..780f7ea426f 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -346,8 +346,16 @@ fn associated_type_for_impl_trait_in_impl( ) -> LocalDefId { let impl_local_def_id = tcx.local_parent(impl_fn_def_id); - // FIXME fix the span, we probably want the def_id of the return type of the function - let span = tcx.def_span(impl_fn_def_id); + let decl = tcx + .hir() + .find_by_def_id(impl_fn_def_id) + .expect("expected item") + .fn_decl() + .expect("expected decl"); + let span = match decl.output { + hir::FnRetTy::DefaultReturn(_) => tcx.def_span(impl_fn_def_id), + hir::FnRetTy::Return(ty) => ty.span, + }; let impl_assoc_ty = tcx.at(span).create_def(impl_local_def_id, DefPathData::ImplTraitAssocTy); let local_def_id = impl_assoc_ty.def_id(); diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index be063a097c0..7eb1042d2f8 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -1,13 +1,22 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_middle::middle::resolve_bound_vars as rbv; use rustc_middle::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; use std::iter; pub fn provide(providers: &mut Providers) { - *providers = Providers { assumed_wf_types, ..*providers }; + *providers = Providers { + assumed_wf_types, + assumed_wf_types_for_rpitit: |tcx, def_id| { + assert!(tcx.is_impl_trait_in_trait(def_id.to_def_id())); + tcx.assumed_wf_types(def_id) + }, + ..*providers + }; } fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] { @@ -42,6 +51,81 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' let mut impl_spans = impl_spans(tcx, def_id); tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap()))) } + DefKind::AssocTy if let Some(data) = tcx.opt_rpitit_info(def_id.to_def_id()) => match data { + ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id } => { + let hir::OpaqueTy { lifetime_mapping, .. } = + *tcx.hir().expect_item(opaque_def_id.expect_local()).expect_opaque_ty(); + // We need to remap all of the late-bound lifetimes in theassumed wf types + // of the fn (which are represented as ReFree) to the early-bound lifetimes + // of the RPITIT (which are represented by ReEarlyBound owned by the opaque). + // Luckily, this is very easy to do because we already have that mapping + // stored in the HIR of this RPITIT. + // + // Side-note: We don't really need to do this remapping for early-bound + // lifetimes because they're already "linked" by the bidirectional outlives + // predicates we insert in the `explicit_predicates_of` query for RPITITs. + let mut mapping = FxHashMap::default(); + let generics = tcx.generics_of(def_id); + for &(lifetime, new_early_bound_def_id) in + lifetime_mapping.expect("expected lifetime mapping for RPITIT") + { + if let Some(rbv::ResolvedArg::LateBound(_, _, def_id)) = + tcx.named_bound_var(lifetime.hir_id) + { + let name = tcx.hir().name(lifetime.hir_id); + let index = generics + .param_def_id_to_index(tcx, new_early_bound_def_id.to_def_id()) + .unwrap(); + mapping.insert( + ty::Region::new_free( + tcx, + fn_def_id, + ty::BoundRegionKind::BrNamed(def_id, name), + ), + ty::Region::new_early_bound( + tcx, + ty::EarlyBoundRegion { + def_id: new_early_bound_def_id.to_def_id(), + index, + name, + }, + ), + ); + } + } + // FIXME: This could use a real folder, I guess. + let remapped_wf_tys = tcx.fold_regions( + tcx.assumed_wf_types(fn_def_id.expect_local()).to_vec(), + |region, _| { + // If `region` is a `ReFree` that is captured by the + // opaque, remap it to its corresponding the early- + // bound region. + if let Some(remapped_region) = mapping.get(®ion) { + *remapped_region + } else { + region + } + }, + ); + tcx.arena.alloc_from_iter(remapped_wf_tys) + } + // Assumed wf types for RPITITs in an impl just inherit (and instantiate) + // the assumed wf types of the trait's RPITIT GAT. + ty::ImplTraitInTraitData::Impl { .. } => { + let impl_def_id = tcx.local_parent(def_id); + let rpitit_def_id = tcx.associated_item(def_id).trait_item_def_id.unwrap(); + let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto( + tcx, + impl_def_id.to_def_id(), + tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity().args, + ); + tcx.arena.alloc_from_iter( + ty::EarlyBinder::bind(tcx.assumed_wf_types_for_rpitit(rpitit_def_id)) + .iter_instantiated_copied(tcx, args) + .chain(tcx.assumed_wf_types(impl_def_id).into_iter().copied()), + ) + } + }, DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)), DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) { DefKind::TyAlias => ty::List::empty(), @@ -51,7 +135,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself // and `&'static T`. DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"), - def_kind @ _ => { + def_kind => { bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}") } }, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 16b1836ba9f..a21b5ef05e6 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -2,7 +2,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; -use rustc_middle::traits::CodegenObligationError; +use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt}; use rustc_span::sym; @@ -177,12 +177,15 @@ fn resolve_associated_item<'tcx>( Some(ty::Instance::new(leaf_def.item.def_id, args)) } - traits::ImplSource::Object(ref data) => { - traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| { - Instance { def: ty::InstanceDef::Virtual(trait_item_id, index), args: rcvr_args } - }) + traits::ImplSource::Builtin(BuiltinImplSource::Object { vtable_base }, _) => { + traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map( + |index| Instance { + def: ty::InstanceDef::Virtual(trait_item_id, index), + args: rcvr_args, + }, + ) } - traits::ImplSource::Builtin(..) => { + traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => { let lang_items = tcx.lang_items(); if Some(trait_ref.def_id) == lang_items.clone_trait() { // FIXME(eddyb) use lang items for methods instead of names. @@ -290,7 +293,9 @@ fn resolve_associated_item<'tcx>( None } } - traits::ImplSource::Param(..) | traits::ImplSource::TraitUpcasting(_) => None, + traits::ImplSource::Param(..) + | traits::ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | traits::ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => None, }) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index b840ff184e0..3500c2cc370 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -96,6 +96,13 @@ fn layout_of_uncached<'tcx>( cx: &LayoutCx<'tcx, TyCtxt<'tcx>>, ty: Ty<'tcx>, ) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> { + // Types that reference `ty::Error` pessimistically don't have a meaningful layout. + // The only side-effect of this is possibly worse diagnostics in case the layout + // was actually computable (like if the `ty::Error` showed up only in a `PhantomData`). + if let Err(guar) = ty.error_reported() { + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + let tcx = cx.tcx; let param_env = cx.param_env; let dl = cx.data_layout(); @@ -564,11 +571,15 @@ fn layout_of_uncached<'tcx>( return Err(error(cx, LayoutError::Unknown(ty))); } - ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => { + ty::Bound(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Infer(_) + | ty::Error(_) => { bug!("Layout::compute: unexpected type `{}`", ty) } - ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => { + ty::Placeholder(..) | ty::Param(_) => { return Err(error(cx, LayoutError::Unknown(ty))); } }) diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 55b8857ed39..147b600f7ba 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -8,6 +8,7 @@ #![feature(assert_matches)] #![feature(iterator_try_collect)] #![feature(let_chains)] +#![feature(if_let_guard)] #![feature(never_type)] #![feature(box_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 3e6dfc1304f..f89558a4599 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -96,7 +96,7 @@ where return Some(Err(AlwaysRequiresDrop)); } - let components = match needs_drop_components(ty, &tcx.data_layout) { + let components = match needs_drop_components(tcx, ty) { Err(e) => return Some(Err(e)), Ok(components) => components, }; @@ -133,7 +133,7 @@ where _ => { tcx.sess.delay_span_bug( tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP), - format!("unexpected generator witness type {:?}", witness), + format!("unexpected generator witness type {witness:?}"), ); return Some(Err(AlwaysRequiresDrop)); } @@ -160,7 +160,7 @@ where queue_type(self, required); } } - ty::Array(..) | ty::Alias(..) | ty::Param(_) => { + ty::Alias(..) | ty::Array(..) | ty::Placeholder(_) | ty::Param(_) => { if ty == component { // Return the type to the caller: they may be able // to normalize further than we can. @@ -172,7 +172,31 @@ where queue_type(self, component); } } - _ => return Some(Err(AlwaysRequiresDrop)), + + ty::Foreign(_) | ty::Dynamic(..) => { + return Some(Err(AlwaysRequiresDrop)); + } + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Slice(_) + | ty::Ref(..) + | ty::RawPtr(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Tuple(_) + | ty::Bound(..) + | ty::GeneratorWitness(..) + | ty::GeneratorWitnessMIR(..) + | ty::Never + | ty::Infer(_) + | ty::Error(_) => { + bug!("unexpected type returned by `needs_drop_components`: {component}") + } } } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 505f78d0e5f..791a1f4fa99 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -129,7 +129,9 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // sure that this will succeed without errors anyway. if tcx.def_kind(def_id) == DefKind::AssocFn - && tcx.associated_item(def_id).container == ty::AssocItemContainer::TraitContainer + && let assoc_item = tcx.associated_item(def_id) + && assoc_item.container == ty::AssocItemContainer::TraitContainer + && assoc_item.defaultness(tcx).has_value() { let sig = tcx.fn_sig(def_id).instantiate_identity(); // We accounted for the binder of the fn sig, so skip the binder. @@ -144,85 +146,9 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { } let local_did = def_id.as_local(); - // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): This isn't correct for - // RPITITs in const trait fn. - let hir_id = local_did.and_then(|def_id| tcx.opt_local_def_id_to_hir_id(def_id)); - - // FIXME(consts): This is not exactly in line with the constness query. - let constness = match hir_id { - Some(hir_id) => match tcx.hir().get(hir_id) { - hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) - if tcx.is_const_default_method(def_id) => - { - hir::Constness::Const - } - - hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(..), .. - }) - | hir::Node::AnonConst(_) - | hir::Node::ConstBlock(_) - | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) - | hir::Node::ImplItem(hir::ImplItem { - kind: - hir::ImplItemKind::Fn( - hir::FnSig { - header: hir::FnHeader { constness: hir::Constness::Const, .. }, - .. - }, - .., - ), - .. - }) => hir::Constness::Const, - - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..), - .. - }) => { - let parent_hir_id = tcx.hir().parent_id(hir_id); - match tcx.hir().get(parent_hir_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) => *constness, - _ => span_bug!( - tcx.def_span(parent_hir_id.owner), - "impl item's parent node is not an impl", - ), - } - } - - hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..), - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: - hir::TraitItemKind::Fn( - hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, - .., - ), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), - .. - }) => *constness, - - _ => hir::Constness::NotConst, - }, - // FIXME(consts): It's suspicious that a param-env for a foreign item - // will always have NotConst param-env, though we don't typically use - // that param-env for anything meaningful right now, so it's likely - // not an issue. - None => hir::Constness::NotConst, - }; let unnormalized_env = - ty::ParamEnv::new(tcx.mk_clauses(&predicates), traits::Reveal::UserFacing, constness); + ty::ParamEnv::new(tcx.mk_clauses(&predicates), traits::Reveal::UserFacing); let body_id = local_did.unwrap_or(CRATE_DEF_ID); let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id); @@ -255,8 +181,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> { fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> { if let ty::Alias(ty::Projection, unshifted_alias_ty) = *ty.kind() - && self.tcx.is_impl_trait_in_trait(unshifted_alias_ty.def_id) - && self.tcx.impl_trait_in_trait_parent_fn(unshifted_alias_ty.def_id) == self.fn_def_id + && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. } + | ty::ImplTraitInTraitData::Impl { fn_def_id, .. }) + = self.tcx.opt_rpitit_info(unshifted_alias_ty.def_id) + && fn_def_id == self.fn_def_id && self.seen.insert(unshifted_alias_ty.def_id) { // We have entered some binders as we've walked into the diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index a7cd7aa2c4f..c2655d68b79 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -408,7 +408,7 @@ pub fn debug_bound_var<T: std::fmt::Write>( var: impl std::fmt::Debug, ) -> Result<(), std::fmt::Error> { if debruijn == INNERMOST { - write!(fmt, "^{:?}", var) + write!(fmt, "^{var:?}") } else { write!(fmt, "^{}_{:?}", debruijn.index(), var) } diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index b134845dab0..ec0dbffc22f 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -39,6 +39,7 @@ pub enum AliasKind { /// A projection `<Type as Trait>::AssocType`. /// Can get normalized away if monomorphic enough. Projection, + /// An associated type in an inherent `impl` Inherent, /// An opaque type (usually from `impl Trait` in type aliases or function return types) /// Can only be normalized away in RevealAll mode |
